返回信息流开门见山地说,小程序在日常开发中使用原生框架来开发还是挺不方便的,比如:
* 不支持 `npm` 包
* 不支持各种 `CSS` 预编译器
* 不支持配置 `Babel` 来转换一些 `JavaScript` 新特性
这样一来和日常开发前端页面的体验相比来说,简直就像在**刀耕火种**。
> 那么为了解决这些问题,我们能不能将前端开发中常用的 `webpack` 移植到小程序开发中呢?
**当然可以!**
https://webpack.js.org/assets/icon-square-big.svg
## 0.源码地址
https://img.shields.io/badge/webpack-%5E4.8.1-green.svg
https://img.shields.io/badge/vue--loader-%5E15.0.12-green.svg
* 在 [webpack-simple]((https://github.com/tuateam/tua-mp/tree/master/examples/webpack-simple/)) 中文件结构和小程序相似。
* 而在 [webpack-vue]((https://github.com/tuateam/tua-mp/tree/master/examples/webpack-vue/)) 中还增加了 `vue-loader`,因此你甚至还能利用 `.vue` 文件编写单文件组件。
https://buptsteve.github.io/blog/imgs/tua-mp/logs.vue.png
## 1.文件结构
既然用 `webpack` 来编译源代码,那么很自然的我们的文件结构首先要分为 `src/` 和 `dist/`,用开发者工具打开的应该是 `dist/` 目录。
### 1.1.`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
```
* app/: 应用入口
* assets/: 资源文件,比如图片
* comps/: 组件
* pages/: 页面
* scripts: 公用代码
* scripts/const: 常量(已配置别名 @const)
* scripts/utils: 辅助函数(已配置别名 @utils)
* styles/: 公用样式
* templates/: 模板
### 1.2.`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
├── project.config.json
└── templates
└── info.wxml
```
* chunks/: 公共依赖
* runtime: [是 webapck 在运行时连接各个模块的代码](https://doc.webpack-china.org/concepts/manifest/#runtime)
* vendors: 是提取的 `node_modules` 下的依赖
* scripts: 是提取的 `src/scripts/` 下的依赖
### 1.3.整个项目文件结构大概长这样:
```
.
├── README.md
├── dist/
├── package.json
├── project.config.json
├── src/
├── webpack.config.babel.js
└── yarn.lock
```
* src/: 源码
* dist/: 打包后代码
## 2.webpack 基础配置
## 2.1.entry/output
小程序场景下的配置应该是多入口,主要分为 `app`、`pages`、`comps` 这三类。
* app: 将 `src/app/` 下的文件编译成 `dist/` 根目录下的 `app.js/app.json/app.wxss`
* pages: `src/pages/ -> dist/pages/`
* comps: `src/comps/ -> dist/comps/`
在输出 `output` 部分有个坑:因为小程序使用的是 `global`,所以必须添加配置 `output.globalObject` 为 `global`。
> 不然...
```js
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 || []
```
[详情可参阅这个 pr](https://github.com/webpack/webpack/pull/6200)
> ps 在 mpvue 中似乎是通过修改 target 实现的... http://mpvue.com/build/mpvue-webpack-target/
## 2.2.CommonChunk
在 webpack 4 中有一个 breaking change,[即使用 `SplitChunksPlugin` 替换了之前很常用的 `CommonsChunkPlugin`](https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366)
主要提取了三部分的公共代码:
* runtime: [是 webapck 在运行时连接各个模块的代码](https://doc.webpack-china.org/concepts/manifest/#runtime)
* vendors: 是提取的 `node_modules` 下的依赖
* scripts: 是提取的 `src/scripts/` 下的依赖
> 现在又碰到个新的问题:如何引入这些 `chunks`?
在前端项目中一般我们通过 `HtmlWebpackPlugin` 插件在 html 文件中添加 `<script>` 标签引入,然鹅小程序中并没有 html 文件...
> 计将安出?
总不能每次都手动去 `dist/app.js` 中 require 这些文件吧?
这时候就要介绍另一款插件了~:`BannerPlugin`。
这个插件本来是用在文件头部添加 banner 的,但是也支持插入代码,因此利用这款插件我们就可以将这些公共依赖在 `app.js` 中统一引入一次即可。
> TODO: 现版本的小程序提供了分包加载能力,因此这里还有优化空间
## 2.3.CopyWebpackPlugin
顾名思义,这款插件的用处就是拷贝,利用这款插件我们就可以实现:
* 复制 `project.config.json`
* 复制 `*.json`
* 复制 `*.wxml`
* 复制 `*.wxss`
* 复制 `assets/`
* 复制 `templates/`
在使用时有一个知识点可以减少代码量:即 `context` 选项,这样就不用写 n 个 `src/`了...
```js
new CopyWebpackPlugin(copyCfgArr, {
context: resolve('src'),
}),
```
## 2.4.预处理器和 CSS 的处理
这部分其实都是常规操作和一般 web 开发没啥区别,配置好对应的 loader 即可。
需要注意的点就是一定要使用 `ExtractTextWebpackPlugin` 插件来生成 `.wxss` 文件。
```js
new ExtractTextPlugin('[name].wxss')
```
## 3.webpack + vue-loader
这部分谈谈如何利用 `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 项目很相似了~
### 3.1.vue-loader v15?
随着 webpack 升级到了 v4,官方与之配合的 `vue-loader` 也升级到了 v15。
> 现在 Vue Loader 15 使用了一个不一样的策略来推导语言块使用的 loader。
> 在 v15 中,`<style lang="less">` 会完成把它当作一个真实的 `*.less` 文件来加载。因此,为了这样处理它,你需要在你的主 webpack 配置中显式地提供一条规则。
简单来说就是咱们之前配置过的各个预处理器规则会被 `vue-loader` 自动使用。
因此我们只需要简单地添加一条规则即可读取 `.vue` 文件:
```js
{
test: /\.vue$/,
exclude: /node_modules/,
loader: 'vue-loader',
options: {
compiler: {
// mock vue-template-compiler
compile: () => ({
staticRenderFns: [],
})
},
},
},
```
> `options.compiler` 是啥?
### 3.2.options.compiler
[options.compiler 覆写用来编译单文件组件中 `<template>` 块的默认编译器。](https://vue-loader.vuejs.org/zh/options.html#compiler)
在实际使用单文件组件时,我们通过 `<template lang="wxml">` 来包裹原本的 `.wxml` 文件中的内容。
因为最终要编译成 `.wxml` 文件才能被开发者工具识别,所以我们还编写了一条规则通过 `file-loader` 生成最终的 `.wxml` 文件:
```js
{
// 处理 <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`。
### 3.3.Custom Blocks
> 最后还有个问题没有解决:如何处理 `.json` 文件?
在其他的小程序框架中是这样处理的:
* 在 `wepy` 中将其作为组件的 `config` 属性
```js
export default class Index extends wepy.page {
//页面配置
config = {
"navigationBarTitleText": "test"
};
// ...
}
```
* 在 `mpvue` 中是写在 `main.js` 的输出部分
```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>` 块来编写配置。
```html
<config>
{
"navigationBarTitleText": "查看启动日志"
}
</config>
<template lang="wxml">
...
</template>
```
但是并没有将 `app.json` 的内容放到 `App.vue` 中,因为有时需要读取这里的页面配置。如果写到 `<config>` 中的话,就无法读取了...
> 例如为了实现从分享后的页面后退返回首页这个功能,在辅助函数中就需要读取页面和 tabBar 配置,生成分享链接(实际分享地址是首页,然后从首页再导航到被分享的页面)。
因此最优解是页面配置写在 `<config>` 中,应用配置写在 `app.js` 的输出中。
> TODO: 实现 mpvue 的方式处理 `app.json`
具体的配置如下:
```js
{
// 处理 <config>{...}</config> 代码块
// 生成 .json 文件
resourceQuery: /blockType=config/,
use: {
loader: 'file-loader',
options: {
name: getNameByFilePathAndExt('.json'),
},
},
},
```
## 4.总结
综上,咱们在 `webpack v4` 和 `vue-loader v15` 的帮助下,让小程序拥有了以下能力:
* 加载 npm 包
* 提取 CommonChunk 减少打包体积
* babel 编译 JavaScript 代码
* 支持 less/sass/stylus 等预处理器
* 单文件组件
> 不过话又说回来了...
> 原生的小程序...又不是不能用~
https://buptsteve.github.io/blog/imgs/tua-mp/又不是不能用.jpg
> 注:这句话是黄章说的,Teacher Luo 没说过这话哟~
以上 to be continued...
这是一条镜像帖。来源:北邮人论坛 / java-script / #4188同步于 2018/8/16
JavaScript机器人发帖
【讨论】【心得】终极蛇皮上帝视角之微信小程序之告别“刀耕火
stevesasuke
2018/8/16镜像同步0 回复
订阅后,新回复会通过你的通知中心匿名送达。
0 条回复
暂无回复 · 你可以订阅本帖等待新回复。