開門見山地說,小程序在平常開發中使用原生框架來開發仍是挺不方便的,好比:css
npm
包CSS
預編譯器Babel
來轉換一些 JavaScript
新特性這樣一來和平常開發前端頁面的體驗相比來講,簡直就像在刀耕火種。html
那麼爲了解決這些問題,咱們能不能將前端開發中經常使用的
webpack
移植到小程序開發中呢?
固然能夠!前端
vue-loader
,所以你甚至還能利用 .vue
文件編寫單文件組件。注:已封裝到 https://tuateam.github.io/tua... 中...
既然用 webpack
來編譯源代碼,那麼很天然的咱們的文件結構首先要分爲 src/
和 dist/
,開發者工具的目標應該是 dist/
目錄。vue
注:開發者工具打開的應該是根目錄,這樣能夠保存各類設置,能夠在project.config.json
中配置"miniprogramRoot": "./dist/",
src/
中文件結構大概長這樣:. ├── app │ ├── app.js │ ├── app.json │ └── app.scss ├── assets │ └── vue-logo.png ├── comps │ └── todo │ ├── todo.js │ ├── todo.json │ ├── todo.less │ └── todo.wxml ├── pages │ └── index │ ├── index.js │ ├── index.json │ ├── index.less │ └── index.wxml ├── scripts │ ├── const │ │ ├── README.md │ │ └── index.js │ └── utils │ ├── README.md │ ├── event.js │ ├── format.js │ ├── index.js │ └── log.js ├── styles │ ├── global.styl │ ├── todomvc-app-css.css │ └── todomvc-common-base.css └── templates └── info.wxml
dist/
中文件結構大概長這樣:. ├── app.js ├── app.js.map ├── app.json ├── app.wxss ├── assets │ └── vue-logo.png ├── chunks │ ├── runtime.js │ ├── runtime.js.map │ ├── scripts.js │ ├── scripts.js.map │ ├── vendors.js │ └── vendors.js.map ├── comps │ └── todo │ ├── todo.js │ ├── todo.js.map │ ├── todo.json │ ├── todo.wxml │ └── todo.wxss ├── pages │ └── index │ ├── index.js │ ├── index.js.map │ ├── index.json │ ├── index.wxml │ └── index.wxss └── templates └── info.wxml
chunks/: 公共依賴node
node_modules
下的依賴src/scripts/
下的依賴. ├── README.md ├── dist/ ├── package.json ├── project.config.json ├── src/ ├── webpack.config.babel.js └── yarn.lock
小程序場景下的配置應該是多入口,主要分爲 app
、pages
、comps
這三類。webpack
src/app/
下的文件編譯成 dist/
根目錄下的 app.js/app.json/app.wxss
src/pages/ -> dist/pages/
src/comps/ -> dist/comps/
在輸出 output
部分有個坑:由於小程序使用的是 global
,因此必須添加配置 output.globalObject
爲 global
。git
否則...
thirdScriptError VM937:1 sdk uncaught third Error Cannot read property 'webpackJsonp' of undefined TypeError: Cannot read property 'webpackJsonp' of undefined at http://127.0.0.1:40247/appservice/chunks/runtime.js:34:51 at http://127.0.0.1:40247/appservice/chunks/runtime.js:38:2 at require (http://127.0.0.1:40247/appservice/__dev__/WAService.js:19:7859) at http://127.0.0.1:40247/appservice/__dev__/WAService.js:19:7573 at http://127.0.0.1:40247/appservice/app.js:3:1 at require (http://127.0.0.1:40247/appservice/__dev__/WAService.js:19:7859) at http://127.0.0.1:40247/appservice/appservice?t=1527755092895:1020:9 // runtime var a = window.webpackJsonp = window.webpackJsonp || []
詳情可參閱這個 prgithub
ps 在 mpvue 中彷佛是經過修改 target 實現的... http://mpvue.com/build/mpvue-...
在 webpack 4 中有一個 breaking change,即便用 SplitChunksPlugin
替換了以前很經常使用的 CommonsChunkPlugin
web
主要提取了三部分的公共代碼:npm
node_modules
下的依賴src/scripts/
下的依賴
如今又碰到個新的問題:如何引入這些
chunks
?
在前端項目中通常咱們經過 HtmlWebpackPlugin
插件在 html 文件中添加 <script>
標籤引入,然鵝小程序中並無 html 文件...
計將安出?
總不能每次都手動去 dist/app.js
中 require 這些文件吧?
這時候就要介紹另外一款插件了~:BannerPlugin
。
這個插件原本是用在文件頭部添加 banner 的,可是也支持插入代碼,所以利用這款插件咱們就能夠將這些公共依賴在 app.js
中統一引入一次便可。
TODO: 現版本的小程序提供了分包加載能力,所以這裏還有優化空間
顧名思義,這款插件的用處就是拷貝,利用這款插件咱們就能夠實現:
*.json
*.wxml
*.wxss
assets/
templates/
在使用時有一個知識點能夠減小代碼量:即 context
選項,這樣就不用寫 n 個 src/
了...
new CopyWebpackPlugin(copyCfgArr, { context: resolve('src'), }),
這部分其實都是常規操做和通常 web 開發沒啥區別,配置好對應的 loader 便可。
須要注意的點就是必定要使用 ExtractTextWebpackPlugin
插件來生成 .wxss
文件。
new ExtractTextPlugin('[name].wxss')
注:已換成
mini-css-extract-plugin
這部分談談如何利用 vue-loader
實如今小程序中引用單文件組件(.vue
)。
先看看 src/
下的文件結構:
. ├── app │ ├── App.vue │ ├── app.js │ └── app.json ├── assets │ └── vue-logo.png ├── comps │ ├── Filter │ │ ├── Filter.vue │ │ └── index.js │ └── Todo │ ├── Todo.vue │ └── index.js ├── pages │ ├── index │ │ ├── Index.vue │ │ └── index.js │ └── todos │ ├── Todos.vue │ └── index.js ├── scripts │ ├── const │ │ ├── README.md │ │ └── index.js │ └── utils │ ├── README.md │ ├── event.js │ ├── format.js │ ├── index.js │ └── log.js ├── styles │ ├── global.styl │ ├── todomvc-app-css.css │ └── todomvc-common-base.css └── templates └── info.wxml
其實已經和通常的 web 項目很類似了~
隨着 webpack 升級到了 v4,官方與之配合的 vue-loader
也升級到了 v15。
如今 Vue Loader 15 使用了一個不同的策略來推導語言塊使用的 loader。在 v15 中,
<style lang="less">
會完成把它看成一個真實的*.less
文件來加載。所以,爲了這樣處理它,你須要在你的主 webpack 配置中顯式地提供一條規則。
簡單來講就是我們以前配置過的各個預處理器規則會被 vue-loader
自動使用。
所以咱們只須要簡單地添加一條規則便可讀取 .vue
文件:
{ test: /\.vue$/, exclude: /node_modules/, loader: 'vue-loader', options: { compiler: { // mock vue-template-compiler compile: () => ({ staticRenderFns: [], }) }, }, },
options.compiler
是啥?
注意:隨着 vue-loader 的升級,這部分的 mock 有變化...
options: { // mock vue-template-compiler compile: () => ({ staticRenderFns: [], }), parseComponent: require('vue-template-compiler') .parseComponent, }, },
options.compiler 覆寫用來編譯單文件組件中 <template>
塊的默認編譯器。
在實際使用單文件組件時,咱們經過 <template lang="wxml">
來包裹本來的 .wxml
文件中的內容。
由於最終要編譯成 .wxml
文件才能被開發者工具識別,因此咱們還編寫了一條規則經過 file-loader
生成最終的 .wxml
文件:
{ // 處理 <template lang="wxml">{...}</template> // 生成 .wxml 文件 test: /\.wxml$/, use: { loader: 'file-loader', options: { name: getNameByFilePathAndExt('.wxml'), }, }, },
可是由於 vue-loader
默認會編譯 template 中的內容將其生成一個個 render 函數。但其實在小程序場景中咱們並不須要這一步驟。咱們只想安安靜靜地將這些代碼經過 file-loader
生成 .wxml
文件...
幸虧 vue-loader
還提供了 options.compiler
這個參數用來傳遞本身的編譯器。因此這裏實際上是 mock 了一下 vue-template-compiler
。
最後還有個問題沒有解決:如何處理
.json
文件?
在其餘的小程序框架中是這樣處理的:
wepy
中將其做爲組件的 config
屬性export default class Index extends wepy.page { //頁面配置 config = { "navigationBarTitleText": "test" }; // ... }
mpvue
中是寫在 main.js
的輸出部分// main.js export default { // 這個字段走 app.json config: { // 頁面前帶有 ^ 符號的,會被編譯成首頁,其餘頁面能夠選填,咱們會自動把 webpack entry 裏面的入口頁面加進去 pages: ['pages/logs/main', '^pages/index/main'], window: { backgroundTextStyle: 'light', navigationBarBackgroundColor: '#fff', navigationBarTitleText: 'WeChat', navigationBarTextStyle: 'black' } } } // src/pages/logs/main.js export default { config: { navigationBarTitleText: '查看啓動日誌' } }
在 tua-mp
中目前採用的是自定義塊的方式來實現的,即在 .vue
文件中新增了一個 <config>
塊來編寫配置。
<config> { "navigationBarTitleText": "查看啓動日誌" } </config> <template lang="wxml"> ... </template>
可是並無將 app.json
的內容放到 App.vue
中,由於有時須要讀取這裏的頁面配置。若是寫到 <config>
中的話,就沒法讀取了...
例如爲了實現從分享後的頁面後退返回首頁這個功能,在輔助函數中就須要讀取頁面和 tabBar 配置,生成分享連接(實際分享地址是首頁,而後從首頁再導航到被分享的頁面)。
所以最優解是頁面配置寫在 <config>
中,應用配置寫在 app.js
的輸出中。
TODO: 實現 mpvue 的方式處理
app.json
具體的配置以下:
{ // 處理 <config>{...}</config> 代碼塊 // 生成 .json 文件 resourceQuery: /blockType=config/, use: { loader: 'file-loader', options: { name: getNameByFilePathAndExt('.json'), }, }, },
綜上,我們在 webpack v4
和 vue-loader v15
的幫助下,讓小程序擁有了如下能力:
不過話又說回來了...原生的小程序...又不是不能用~
注:這句話是黃章說的,Teacher Luo 沒說過這話喲~
以上 to be continued...