您好,登錄后才能下訂單哦!
小編給大家分享一下webpack-build-miniprogram有什么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
webpack-build-miniprogram 是 Medusa 方案的基礎也是核心,這一工具包提供了以 webpack 構建微信小程序的能力,并且我們可以利用 webpack 的生態持續豐富 Medusa 的功能。在講述基礎構建配置之前,我們先來看看 Medusa 的目錄結構基礎,有了相應的目錄約束才使得項目更加規范化。
|-- dist 編譯結果目錄 |-- src 源代碼目錄 | |-- app.js 項目入口文件 | |-- app.json 小程序配置文件 | |-- sitemap.json sitemap配置文件 | |-- assets 靜態資源存放目錄 | | |-- .gitkeep | |-- components 公共組件存放目錄 | | |-- .gitkeep | |-- dicts 公共字典存放目錄 | | |-- .gitkeep | |-- libs 第三方工具庫存放目錄(外部引入) | | |-- .gitkeep | |-- pages 頁面文件存放目錄 | | |-- index | | |-- index.js | | |-- index.json | | |-- index.less | | |-- index.wxml | |-- scripts 公共腳本存放目錄(wxs) | | |-- .gitkeep | |-- services API服務存放目錄 | | |-- .gitkeep | |-- styles | | |-- index.less 項目總通用樣式 | | |-- theme.less 項目主題樣式 | |-- templates 公共模板存放目錄 | | |-- .gitkeep | |-- utils 公共封裝函數存放目錄(自我封裝) | |-- .gitkeep |-- .env 環境變量配置文件 |-- config.yaml 編譯配置文件 |-- webpack.config.js webpack 配置擴展文件 |-- project.config.json 開發者工具配置文件 └── package.json復制代碼
webpack 這一工具現在已經成為前端工程師的必備技能,復雜的工作原理讓我們對它總是有種敬畏感,所以在做微信小程序構建策略過程中,我們先將它簡單的理解為一個“搬運工具”。它將源代碼目錄中的文件加以某些處理之后再輸出到目標目錄中。現在我們明確一下我們要搬運哪些文件,微信小程序中涉及到的主要有:
邏輯文件 .js
配置文件 .json
模板文件 .wxml
樣式文件 .wxss
.less
.scss
腳本文件 .wxs
靜態資源文件 assets/
接下來我們將書寫 webpack 的公共部分配置,利用 copy-webpack-plugin 這一插件來完成大部分文件的搬運工作。
/** config/webpack.common.js */const CopyPlugin = require("copy-webpack-plugin");const config = { context: SOURCE, devtool: 'none', entry: { app: './app.js' }, output: { filename: '[name].js', path: DESTINATION }, plugins: [ new CopyPlugin([ { from: 'assets/', to: 'assets/', toType: 'dir' }, { from: '**/*.wxml', toType: 'dir' }, { from: '**/*.wxss', toType: 'dir' }, { from: '**/*.json', toType: 'dir' }, { from: '**/*.wxs', toType: 'dir' } ]) ] };復制代碼
以上簡單的配置我們就實現了除邏輯文件與預編譯語言文件以外的搬運工作,在配置中出現了 SOURCE
、 DESTINATION
兩個常量,它們分別代表的是源代碼目錄與目標代碼目錄的絕對路徑,我們將它們抽離在單獨的字典文件中:
/** libs/dicts.js */const path = require("path"); exports.ROOT = process.cwd(); exports.SOURCE = path.resolve(this.ROOT, 'src'); exports.DESTINATION = path.resolve(this.ROOT, 'dist'); exports.NODE_ENV = process.argv.splice(2, 1)[0];復制代碼
上面搬運的文件因為不需要特殊的內容處理,所以完全交由插件去實現,剩余兩種類型的文件我們就需要使用到 webpack 的入口(entry)、插件(plugin) 和 loader 協同合作才能完成搬運工作。
首先我們要解決如何生成入口的問題,解決了入口生成的問題才能借助 loader 去完成文件內容的轉化。對于入口生成這一問題,我開發了另外一個插件 entry-extract-webpack-plugin 去解決。這一插件我并不打算詳細的講解實現的過程,我只會闡述它的核心實現思路(如果你有興趣進一步了解可以下載下來直接看源碼)。
微信小程序需要建立入口網絡其實是有規律可循的,主包、分包都會配置在 app.json 文件中,頁面所需要的組件也會配置在 [page].json 文件中。抓住這一特點,我們可以將實現插件功能的核心羅列為以下幾點:
通過 node.js
提供的 path
與 fs
模塊功能,以 app.json 文件中配置的路徑為基礎,遞歸的去尋找每個 page 所依賴的 component 路徑,最終整合在同個數組中。
利用 webpack 提供的 SingleEntryPlugin 和 MultiEntryPlugin 插件,在 entryOption 生命周期鉤子中將第一步收集的路徑數組注入到構建當中形成入口(entry)。
構建監聽的過程中如果有新的頁面添加,則通過 watchRun 生命周期將新的入口加入到之前的入口(entry)中。
以上三點是實現生成入口這一功能的核心思路,除了核心的實現思路外,我還想簡單的講解下我們如何去寫一個 webpack 插件:
class EntryExtractPlugin { constructor(options) {} apply(compiler) { compiler.hooks.entryOption.tap('EntryExtractPlugin', () => { ... }); compiler.hooks.watchRun.tap('EntryExtractPlugin', () => { ... }); } }復制代碼
webpack 的插件大致是以類的形式存在,當你使用插件時,它會自動執行 apply 方法, 然后使用 compiler.hooks
對象上的各種生命周期屬性便可以將我們需要的處理邏輯植入到 webpack 的構建流程當中。
上面解決了生成入口(entry)的問題,接下來我們在原有的基礎上完善一下策略。由于預編譯語言的類型較多,我為了策略的可擴展性將樣式部分的策略抽離為單獨的部件,然后在通過 webpack-merge 這一工具將它們合并起來,完整的實現如下:
/** config/webpack.parts.js */exports.loadCSS = ({ reg = /\.css$/, include, exclude, use = [] }) => ({ module: { rules: [ { include, exclude, test: reg, use: [ { loader: require('mini-css-extract-plugin').loader }, { loader: 'css-loader' } ].concat(use) } ] } });復制代碼
/** config/webpack.common.js */const { merge } = require('webpack-merge');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const parts = require('./webpack.parts.js');const config = { ... module: { rules: [ { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ } ] }, plugins: [ ... new MiniCssExtractPlugin({ filename: '[name].wxss' }) ] };module.export = merge([ config, parts.loadCSS({ reg: /\.less$/, use: ['less-loader'] }) ]);復制代碼
以上就是基礎的 webpack 策略,接下來我們書寫一個工具包的可執行文件便可以在項目當中通過 medusa-server {mode}
使用 webpack 提供的功能了。我們需要在工具包的 package.json 文件中配置 bin 字段,它標志了我們通過命令會自動執行哪個文件。
{ ... main: "index.js", bin: { "medusa-server": "index.js" } }復制代碼
/** index.js */const webpack = require('webpack');const { merge } = require('webpack-merge');const chalk = require('chalk');const commonConfig = require('./config/webpack.common');const { NODE_ENV } = require('./libs/dicts');const config = (function(mode) { if (mode === 'production') { return merge([commonConfig, { mode }]); } return merge([commonConfig, { mode: 'development', watch: true, watchOptions: { ignored: /node_modules/ } }]) })(NODE_ENV); webpack(config, (err, stats) => { if (err) { console.log(chalk.red(err.stack || err)); if (err.details) { console.log(chalk.red(err.details)); } return undefined; } const info = stats.toJson(); if (stats.hasErrors()) { console.log(chalk.red(info.errors)); } if (stats.hasWarnings()) { console.log(chalk.yellow(info.warnings)); } });復制代碼
基礎篇當中完成的 webpack 配置已經可以滿足兩點功能,一是對常規文件的輸出,二是具備開發與生產兩種不同的模式。對于基礎篇我還有兩點要說明一下:
為什么沒有應用ES6(更改版本)轉為ES5的相關插件?
因為在實踐當中發現IOS的10.x版本存在async/await語法無法正常使用的情況,所以索性就讓構建更加純粹一些 ,然后啟用微信開發者工具的 ES6 轉 ES5 與 增強編譯 這兩項功能,由官方工具去處理新特性。
為什么 devtool 要設置為 none,難道不需要 sourceMap 嗎?
微信官方已經原生提供了SourceMap功能,這在你上傳版本時開發者工具中就已經有體現了。
接下來就進入進階篇的梳理,在滿足正常的輸出后其實與原生開發好像并沒有太大差異,這完全體現不出 webpack 的作用,所以我們要利用 webpack 的能力及其相關的工具生態來擴展下 Medusa 的功能。接下來我們將賦予 Medusa 以下幾點功能:
路徑別名 @
根據環境自動注入相應的環境域名
ESLint、StyleLint代碼規范檢查
路由功能
公共代碼抽取
環境變量
webpack 可擴展
原生微信小程序只支持相對路徑的引入方式,但是我們難免會遇到必須移動某些文件的情況,假設這個文件在多處被引用,那就頭疼了。所以我們通過 webpack 的能力以及搭配 jsconfig.json 配置文件可以讓我們有更好的開發體驗。
/** config/webpack.common.js */const config = { ... resolve: { alias: { '@': SOURCE } } };復制代碼
{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./src/*"], }, "target": "ES6", "module": "commonjs", "allowSyntheticDefaultImports": true, }, "include": ["src/**/*"], "exclude": ["node_modules"], }復制代碼
通過 alias 別名配置,@ 符號就可以指代 src 源代碼目錄,然后在實際業務項目的根目錄下創建一個 jsconfig.json 文件,通過以上的配置在使用 vscode 編輯器時就可以獲得良好的智能路徑提示功能。
在介紹這個功能之前,我需要先講解一下 config.yaml 這個配置文件的作用。這是我一開始設想用來擴展各種功能的配置文件,它應用在實際業務項目的根目錄中。現階段它的功能還相當少,不過將來應該會逐步迭代增加的。
# 當前項目應用平臺platform: wx# 樣式單位 px 轉換 rpx 的比例設定css_unit_ratio: 1# 域名配置development_host: api: https://www.miniprogram.dev.comproduction_host: api: https://www.miniprogram.pro.com復制代碼
這其中的域名配置是可以自由增減的,書寫形式形如例子當中所示。當我們在其中配置好對應環境的域名后,我們在業務代碼當中便可以用 mc.$hosts.api
變量輕松的訪問域名。在構建過程中工具會自動幫你將對應環境的域名注入代碼當中,你再也不用關心如何去管理和切換不同環境域名的問題了。下面展示一下我是如何實現這一功能的:
/** libs/dicts */exports.CONFIG = path.resolve(this.ROOT, 'config.yaml'); exports.DEFAULT_CONFIG = { platform: 'wx', css_unit_ratio: 1, };復制代碼
/** libs/index.js */const fs = require('fs');const yaml = require('js-yaml');const { CONFIG, DEFAULT_CONFIG } = require('./dicts'); exports.yamlConfig = (function() { try { /** 將 yaml 格式的內容解析為 object 對象 */ const config = yaml.load(fs.readFileSync(CONFIG, { encoding: 'utf-8' })); return config; } catch(e) { return DEFAULT_CONFIG; } })();復制代碼
/** webpack.common.js */const webpack = require('webpack');const { yamlConfig } = require('../libs');const { NODE_ENV } = require('../libs/dicts');const config = { ... plugins: [ ... new webpack.DefinePlugin({ mc: JSON.stringify({ $hosts: yamlConfig[`${NODE_ENV}_host`] || {} }) }) ] };復制代碼
webpack.DefinePlugin
插件可以判別代碼中 mc 這個標識符然后進行相應的替換功能,這樣就能保證代碼的正常運作。
編碼規范已經是老生常談的話題了,今年我所處的前端團隊擴容到二十幾人,在多人共同協作和不同成員維護的情況下,我們迫切的需要統一的編碼規范來減少維護的成本。(在規范落地上,我還有兩句題外話要說,當我們需要落地一項規范的時候,不要停留在討論規則上,也不要妄想用人的自律去約束編碼。如果你的團隊也需要實施這些東西,希望你能夠成為先驅者將規則先定下初版,然后找一個合適的工具,通過工具去約束人的行為。)接下來我們就來看下我們所需要的檢查工具是如何配置的:
/** webpack.common.js */const StylelintPlugin = require('stylelint-webpack-plugin');const config = { module: { rules: [ { enforce: 'pre', test: /\.js$/, exclude: /node_modules/, loader: 'eslint-loader', options: { fix: true, cache: false, formatter: require('eslint-friendly-formatter') } } ] }, plugins: [ new StylelintPlugin({ fix: true, files: '**/*.(sa|sc|le|wx|c)ss' }) ] };復制代碼
以上配置的兩個工具可以對 js 與樣式文件按照一定的規則集進行檢查,對于部分不符合規范的代碼可以自行修復,無法自動修復的部分將會給出相應的提示。應用什么規則集你可以通過業務項目根目錄中的 .eslintrc
和 .stylelintrc
文件去決定(下面的章節我會對規范這一塊再進行相應的展開)。
在應用 webpack 之后,我們為微信小程序開發賦予了 npm 加載依賴的能力。在模塊化打包的機制下,工具包會被加載進引用它的入口當中,這會導致微信小程序包大小及其容易就超出限定值了,所以我們的解決方案是將多次出現的代碼抽離為單獨的公共部分,具體的實施代碼如下:
/** config/webpack.common.js */const config = { ... output: { ..., globalObject: 'global' }, plugins: [ ... new webpack.BannerPlugin({ raw: true, include: 'app.js', banner: 'const vendors = require("./vendors");\nconst commons = require("./commons");\nconst manifest = require("./manifest");' }) ], optimization: { cacheGroups: { vendors: { chunks: 'initial', name: 'vendors', test: /[\\/]node_modules[\\/]/, minChunks: 3, priority: 20 }, commons: { chunks: 'initial', name: 'commons', test: /[\\/](utils|libs|services|apis|models|actions|layouts)[\\/]/, minChunks: 3, priority: 10 } }, runtimeChunk: { name: 'manifest' } } };復制代碼
微信小程序的全局變量可以通過 global 訪問,所以我們需要將 output.globalObject
屬性設置為 global。webpack 內置的 BannerPlugin 插件可以將我們需要的語句插入在指定的文件的頭部,利用它我們就做到了將抽離出來的公共文件重新引入到依賴網格中。
我們在使用 Vue 、React 的腳手架創建的項目時會見到 .env
這個文件,它的主要作用是擴展開發者所需的環境變量。在微信小程序擴展環境變量變量這樣的需求可能少之又少,但是我們可以換一種思路,我們可以利用它來擴展開發者所需的全局變量。接下來我們將利用 dotenv-webpack 這一工具來實現這個功能,它可以讀取業務項目根目錄下的 .env
文件內容,使得我們在編碼中也可以使用當中的變量值。
/** config/webpack.common.js */const Dotenv = require('dotenv-webpack');const { ENV_CONFIG } = require('../libs/dicts');const config = { ... plugins: [ ... new Dotenv({ path: ENV_CONFIG }) ] };復制代碼
通過 webpack 我已經實現了通用的許多功能,但是我所處的公司開發的微信小程序頗多,所以難免有一些項目需要個性化的定制策略。既然有這樣的需求,我們就應該提供這樣的能力給到業務開發者,一來我們可以從多個項目當中吸收更多需要集成的功能點,二來可以暫時減輕自身的負擔。webpack-merge 這一工具其實在前面樣式部分合并我已經使用過了,我們只需要提供業務項目 webpack 配置的路徑即可,再修改下之前的執行文件。
/** index.js */const { WEBPACK_CONFIG } = require('./libs/dicts');const config = (function(mode) { if (mode === 'production') { return merge([commonConfig, { mode }, WEBPACK_CONFIG]); } return merge([commonConfig, { mode: 'development', watch: true, watchOptions: { ignored: /node_modules/ } }, WEBPACK_CONFIG]) })(NODE_ENV); ...復制代碼
微信官方提供了關于路由跳轉的 API ,但我認為官方的 API 在日常開發中有幾點不便:
需要輸入完整的路徑字符串,路徑太長難以記憶不說,假如頁面路徑有所修改需要投入較高的維護成本。
跳轉方式多樣,四種不同類型的跳轉 API 較為常用的是 navigateTo,因為 API 有多個并且參數也較多,所以使用時難免都省不了再去查閱一遍文檔。
官方提供的任何一種 API 最終目標頁面中接收到的 query 都是字符串類型,這一定程度上限制了我們的編碼設計。
為了解決上述的三個問題,我將 API 進行了二次封裝,從中抹除四種跳轉類型的差異,通過統一的接口就可以達到四種跳轉方法的效果。并且通過 webpack 的全局變量注入功能優化了路徑字符串的獲取,方便使用并且容易維護。
mc.$routes
我將頁面的文件夾名稱與路徑相關聯,兩者形成映射關系的話,我們只需要書寫文件夾名稱便可。原來我們需要使用 pages/home/index
訪問 home 頁面,通過我的改造之后,我們可以通過 mc.$routes.home
訪問 home 頁面。
medusa-wx-router
medusa-wx-router 是我對路由功能進行二次封裝后的工具包,具體的實現過程在這里我就不詳述了,你可以自行下載使用或是依照你的需求進行再次改造,下面我只展示一下在業務代碼中結合 mc.$routes
如何使用:
mc.routerTo({ url: mc.$routes.home, type: 'push', query: { id: 0, bool: true }, success: () => console.log('successfully') });/** push 方式快捷形式 */mc.routerTo(mc.$routes.home, { id: 0, bool: true });復制代碼
為了省去 import 路由工具包這一步驟,我使用了 webpack.ProvidePlugin 這一插件自動幫我們在有使用的地方補充 import 功能。
/** config/webpack.common.js */const config = { ... plugins: [ ... new webpack.ProvidePlugin({ 'mc.routerTo': ['medusa-wx-router', 'routerTo'], 'mc.decoding': ['medusa-wx-router', 'decoding'], 'mc.back': ['medusa-wx-router', 'back'], 'mc.goHome': ['medusa-wx-router', 'goHome'], }) ] };復制代碼
規范是工程的重要一環,一個團隊必須遵照同一套規則進行編碼,規范的存在使得代碼的質量得以提升,有統一的規范認知使得成員互相交接項目更加輕松高效。在前面的實踐當中,我已經將規范的檢查工具集成在了構建流程當中,本節我將補充一下應用于微信小程序的規則集配置和相關編輯器插件,當然我還希望你閱讀一下我對于命名規范的一些總結希望對你有所啟發《你可能需要的統一命名規范》。
規則集其實就是一個包含規則的配置文件,接下來我會給出具體的配置內容。當然在考慮到規則集的團隊定制性和升級的問題,我將 ESLint 和 StyleLint 的規則都制作成了 npm 包,這就解決了所有業務項目統一規則的問題。對應的 npm 包分別是 eslint-config-medusa 和 stylelint-config-medusa ,這是我所處的團隊所需要的,所以對于你們在實踐時可以結合你們團隊的現有情況進行改造。
# EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true [*.md] trim_trailing_whitespace = false復制代碼
在業務項目的根目錄中創建 .editorconfig 文件配合 vscode 的 EditorConfig for VS Code 插件便可以對常規的文件進行基礎的編碼約束。
module.exports = { parserOptions: { ecmaVersion: "2018", sourceType: "module" }, parser: "babel-eslint", env: { node: true, commonjs: true, es6: true }, globals: { mc: false, wx: true, swan: true, App: true, Page: true, Component: true, Behavior: true, getApp: true, getCurrentPages: true }, extends: ["airbnb-base"], rules: { // disallow use of console "no-console": "warn", // disallow use of debugger "no-debugger": "error", // disallow dangling underscores in identifiers 'no-underscore-dangle': 'off', // specify the maximum length of a line in your program "max-len": ["error", 120], // require return statements to either always or never specify values "consistent-return": ["error", { "treatUndefinedAsUnspecified": true }], // disallow usage of expressions in statement position "no-unused-expressions": "off" }, settings: { "import/resolver": { alias: { map: [ ["@", "./src"], ["utils", "./src/utils"], ["services", "./src/services"] ], extensions: [".js", ".json", ".less"] } } } };復制代碼
module.exports = { rules: { // Disallow unknown type selectors 'selector-type-no-unknown': null, // Disallow unknown units 'unit-no-unknown': null }, extends: [ 'stylelint-config-standard', 'stylelint-config-recess-order' ], plugins: ['stylelint-order'] };復制代碼
上面展示的就是我所完成的兩個規則集依賴包的入口文件,當我們 install 依賴包之后,我們就可以在業務項目的 .eslintrc 和 .stylelintrc 文件中通過 extends 字段將它們引入。搭配 vscode 編輯器的 ESLint 和 stylelint-plus 插件,它們可以在編碼過程中就提醒你相應的錯誤規則。
前面說的構建與規范全都是服務于具體的業務項目的,在擁有了基礎的能力之后,我們就該思考如何使得業務開發人員能夠快速并且符合要求的進行業務系統開發。讓他們不再需要考慮目錄應該怎么約定,工具如何集成,編碼規范究竟如何應用諸如這些問題。為了達成快速開發這一要求,我著手制作具備初始化項目這一簡易功能的腳手架。最終我將這一工具分為兩個項目,其一是具備初始化項目結構、下載相關依賴包功能的 @chirono/medusa-cli,另外是約定好項目結構與必要配置文件的 miniprogram-base 。
業務開發者只需要通過 npm 全局安裝 @chirono/medusa-cli 工具,便可以通過 medusa create <project-name>
命令初始化一個工程項目,該工具還會提示必要的項目信息讓開發者輸入,用于完善業務項目的 package.json 文件。
以上是“webpack-build-miniprogram有什么用”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。