构建配置

构建配置

从本篇文档开始,我们将介绍 Lavas 构建、运行中使用的配置项。开发者可以在项目根目录下的 lavas.config.js 中定义这些配置项。配置对象的结构大致如下:

12345678// lavas.config.js { build: {}, router: {}, middleware: {}, // 省略其他配置项 }

Lavas 内部使用 Webpack 进行构建,众所周知 Webpack 功能强大但是配置非常复Webpack 杂,我们隐藏了大部分构建细节,将构建流程中部分常用特性以配置项的形式暴露给用户,便于快速上手。同时,对于高级开发者,也能通过特殊的配置项将自定义的 Loader 和 Plugin 加入构建流程。

这些在构建过程中使用的 Webpack 相关配置项将放在 build 下,下面我们将依次介绍这些配置项。

12345678// lavas.config.js build: { ssr: true, path: '', publicPath: '', // 省略其他配置项 }

ssr

切换 SPA 单页应用和 SSR 服务端渲染两种编译模式。

1ssr: true // SSR 模式

path

最终构建产物的输出地址,必须为绝对路径。如下配置将输出构建产物到 dist 文件夹下:

1path: path.resolve(__dirname, 'dist')

等同于 Webpack 配置中的 output.path

publicPath

在静态资源路径之前添加的前缀。默认值为 '/'

1publicPath: '/'

例如在使用 CDN 场景下,可以使用如下配置:

1publicPath: '//cdn.example.com/assets/'

更多使用例子可参考 Webpack 配置中的 output.publicPath

filenames

Webpack 可以指定输出静态资源(JS CSS FONT IMG)的文件名,其中可以使用例如 [hash] 这样的模板字符串。 Lavas 中使用的默认值如下:

123456789filenames: { entry: 'js/[name].[chunkhash:8].js', vue: 'js/vue.[chunkhash:8].js', vendor: 'js/vendor.[chunkhash:8].js', chunk: 'js/[name].[chunkhash:8].js', css: 'css/[name].[contenthash:8].css', img: 'img/[name].[hash:8].[ext]', fonts: 'fonts/[name].[hash:8].[ext]' }

其中:

  • entry entry chunk。将影响各个入口文件名。 可参考 Webpack 中的 output.filename

更多模板字符串示例及其使用场景可以参考 Webpack output.filename。

babel

Lavas 内部配置 Webpack 规则,使用 babel-loader 处理 JS 文件。通过 babel 这个配置项可以指定包括 babel preset 和 plugin 在内的很多属性,更多可配置属性可以参考 babel-loader options

Lavas 默认使用 vue-app 这个 preset,其中已经包含了一系列 babel 插件,在大多数情况下已经能满足 Vue 项目的开发。 但有时我们也需要进行额外的配置,例如在下面使用 vuetify 的场景中,我们需要配置一系列 plugins 以支持组件的按需加载功能:

12345678910111213babel: { plugins: [ "transform-runtime", ["transform-imports", { "vuetify": { "transform": "vuetify/es5/components/${member}", "preventFullImport": true } } ] ] }

使用 .babelrc

如果您更习惯使用根目录下的 .babelrc 来配置 babel-loader,您可以手动开启:

123babel: { babelrc: true }

然后在 .babelrc 中进行配置,例如对于一个 SPA 项目:

123456789{ "presets": [ "vue-app", { "targets": {"ie": 9, "uglify": true} } ], "plugins": [] }

cssExtract

在使用 ExtractTextWebpackPlugin 从 JS 中提取样式时,需要设置 Loader 和 Plugin。由于分离样式会造成额外的编译开销,Lavas 默认在开发模式中关闭这一特性,在生产环境打开。

1cssExtract: true

cssMinimize & cssSourceMap

是否需要对 CSS 文件进行压缩以及生成 source-map。

12cssMinimize: true, cssSourceMap: true

这两个参数最终将传递给 css-loader。

12345loader: 'css-loader', options: { minimize: true, sourceMap: true }

jsSourceMap

是否需要对 JS 文件生成 source-map,便于开发模式调试以及生产环境排查错误,默认开启。

1jsSourceMap: true

Webpack 支持通过 devtool 配置项生成多种 source-map 。这些不同格式的 source-map 在生成速度,是否内联在源文件中等等方面都有显著差异,需要使用者根据具体场景选择合适的格式。 在开发模式中,由于代码经常发生变动,我们通常会选择生成速度快,可以接受内联从而增加源文件体积的代价。而在生产环境中,我们通常选择生成独立的 source-map 文件,此时生成速度就可以忽略了。

在 Lavas 中开启这个配置后将作用于以下两种场景:

  • 开发模式中选择 cheap-module-eval-source-map。这种格式只显示行号不显示列号,重新生成速度很快。

bundleAnalyzerReport

Webpack Bundle Analyzer 提供了可视化图表这样的直观方式,帮助开发者分析构建产物中可能出现的问题,例如重复引入、不必要的依赖。

12345678910// 默认配置,启动 localhost:8888 服务器展示网页 bundleAnalyzerReport: true // 自定义配置 bundleAnalyzerReport: { analyzerMode: 'server', analyzerHost: '127.0.0.1', analyzerPort: 8888, // 省略其他配置 }

Lavas 默认关闭这一配置。开启后,运行 lavas build,将自动启动服务器并打开网页,以下是 Lavas 模板项目的分析结果:

defines

在构建时我们常常需要使用全局常量,在 Webpack 中可以通过 DefinePlugin 插件定义这些常量。Lavas 提供三组命名空间,分别是 SSR 中服务端使用的 server,客户端使用的 client 以及两者共用的 base。另外需要注意的是常量的值必须包含字符串本身内的实际引号,可以使用单引号包含双引号或者 JSON.stringify()

1234567defines: { base: { 'MY_CONSTANT': '"VALUE"' }, client: {}, server: {} }

一个常见的使用场景是,需要根据开发环境和生产环境定义不同的 URL。由于在启动 Lavas 时已经设置了环境变量 process.env.NODE_ENV,在 lavas.config.js 中可以这样做:

12345678910// lavas.config.js const isProd = process.env.NODE_ENV === 'production'; defines: { base: { PASSPORT_URL: isProd ? JSON.stringify('https://wappass.example.com/passport') : JSON.stringify('https://wappass.qatest.example.com/passport') } }

另外,Lavas 已经内置了以下两组全局常量 process.env.VUE_ENVprocess.env.NODE_ENV,可以直接在项目中使用,不需要开发者重复定义:

12345// 在同构应用当前处于 client 或者 server 端 'process.env.VUE_ENV': '"client"', // 当前应用处于开发模式 development 或者生产模式 production 'process.env.NODE_ENV': '"development"'

alias

在 Webpack 中解析模块时,我们常常使用 alias 定义路径的简写别名,便于更加简便地引用模块。

Lavas 提供了 alias 下三组命名空间,分别是 SSR 中服务端使用的 server,客户端使用的 client 以及两者共用的 base

12345alias: { base: {}, client: {}, server: {} }

另外,Lavas 已经内置了两组别名,开发者不需要重复定义。例如如果想引用项目根目录下 components 文件夹中的组件,只需要使用 import MyComponent from '@/components/MyComponent'

12'@': '' // 指向项目根目录, '$': '' // 指向 .lavas 目录

plugins

在使用 Webpack 构建时,各种插件是必不可少的,对于开发者自定义的插件,Lavas 提供了 plugins 下三组命名空间,分别是 SSR 中服务端使用的 server,客户端使用的 client 以及两者共用的 base。有一点需要注意,自定义插件将添加到 Lavas 已有插件列表之后,如果对于插件添加顺序有要求,可以参考 extend 配置项,进行更精确的添加。

12345plugins: { base: [], client: [], server: [] }

extend

为了给予开发者更大的灵活度,能自由修改 Webpack 配置对象,Lavas 提供了 extend 方法。 该方法参数说明如下:

  • config Webpack 配置对象。

例如我们想增加 vue-style-variables-loader 来处理 .vue 文件,可以这么做:

1234567891011121314extend(config, {type, env}) { // 在客户端和服务端同时生效,等同于 type === 'client' || type === 'server' if (type === 'base') { let vueRule = config.module.rules[0]; vueRule.use.push{ loader: 'vue-style-variables-loader', options: { variablesFiles: [ path.join(__dirname, 'assets/styles/variables.styl') ] } } } }

extend 方法适合对于 Webpack 配置对象进行简单扩展的场景,例如添加插件。如果需要对 Lavas 内置的规则和插件进行修改,可以参考下面的 extendByWebpackChain 方法。

extendWithWebpackChain

该方法在 lavas-core-vue@1.0.8 版本引入,同时由于使用了 webpack-chain 依赖,要求 Node 版本 > 6.9.0。

使用上面提到的 extend 方法直接修改 Webpack 配置对象,对于熟悉 Webpack 文档的开发者会比较直观。但是在以下场景下存在局限性:

  • 修改 Lavas 已有规则。由于 Webpack 配置对象层次很深,尤其是访问数组类型的配置项时只能以索引方式。例如 Lavas 内置了 vue-loader 处理 .vue 文件,如果使用 extend 方法试图访问并修改,只能通过 config.module.rules[0].use[0].options.loader 这样的方法,十分繁琐。

而使用 extendWithWebpackChain 可以解决上述三个问题,方法参数和 extend 相同。

扩展 Lavas 内置规则及 Loader

首先,我们给 Lavas 内置的所有规则设置了名称,某条规则应用的任何一个 Loader 都可以通过 config.rule(规则名称).use(Loader 名称) 方式引用,并使用 tap 方法对传入 Loader 的参数进行扩展:

1234567extendWithWebpackChain: (config, {type, env}) => { // 扩展 babel-loader,添加一个 babel 插件 config.module .rule('js') .use('babel') .tap(options => merge(options, { plugins: ['babel-plugin-syntax-object-rest-spread'] }) }

Lavas 内置的全部规则及对应 Loader 如下:

规则Loader 名称说明
vuevue匹配 /\.vue$/ 规则
jsbabel匹配 /\.js$/ 规则,默认使用 vue-app preset
imgurl处理 .png .jpe?g .gif .svg
fonturl处理 .woff2 .eot .ttf .otf
style-csscss匹配 /\.css$/ 规则
style-postcsscss---
style-lesscss less vue-style匹配 /\.less$/ 规则,依次通过 css less 和 vue-style 这三个 Loader 处理,下同
style-sasscss sass vue-style匹配 /\.sass$/ 规则
style-scsscss sass vue-style匹配 /\.scss$/ 规则
style-styluscss stylus vue-style匹配 /\.stylus$/ 规则
style-stylcss stylus vue-style匹配 /\.styl$/ 规则

扩展 Lavas 内置插件

其次,我们给 Lavas 内部使用的所有插件也设置了名称,可以通过 config.plugin(name) 方式访问,通过以下方法可以对插件进行便捷地修改:

  • init 扩展已有插件初始化参数

12345678910extendWithWebpackChain: (config, {type, env}) => { // 在 friendly-error 插件创建时扩展自定义参数 config.plugin('friendly-error').init((Plugin, args) => { let customParams = {}; // 扩展传入插件构造函数的参数 return new Plugin(...args, customParams) } // 添加第三方插件到指定 Lavas 内置 html 插件之后 config.plugin('my-plugin').after('html').use(MyPlugin }

以下是 Lavas 内置的部分插件列表:

插件名称client/server开发/生产环境说明
defineclient & server开发 & 生产DefinePlugin,可以通过 build.defines 进行扩展。
htmlclient开发 & 生产HtmlWebpackPlugin,用于在 SPA 模式下生成 HTML。
skeletonclient开发 & 生产VueSkeletonWebpackPlugin,用于在 SPA 模式下向 HTML 中注入 Skeleton。
chunk-vendorclient开发 & 生产CommonsChunkPlugin,创建包含第三方依赖的 chunk
chunk-vueclient开发 & 生产包含 vue,vuex,vue-router 和 vue-meta
chunk-manifestclient开发 & 生产仅包含 Webpack 运行时代码
hot-module-replacementclient开发代码热更新相关
no-emit-on-errorsclient开发出错时终止编译流程
progress-barclient开发ProgressBarWebpackPlugin,展示构建进度条
friendly-errorclient开发FriendlyErrorsWebpackPlugin,友好地展示错误信息
extract-cssclient & server生产ExtractTextWebpackPlugin,默认在开发模式关闭,生产环境开启。可以通过build.cssExtract配置。
module-concatenationclient & server生产ModuleConcatenationWebpackPlugin,实现预编译功能
hashed-module-idsclient & server生产HashedModuleIdsWebpackPlugin,根据模块的相对路径生成模块 id
optimize-cssclient & server生产OptimizeCSSPlugin,样式去重压缩
uglify-jsclient & server生产UglifyjsWebpackPlugin,JS 压缩
workboxclient生产WorkboxWebpackPlugin,使用 2.x 版本,用于生成 ServiceWorker 文件
sw-registerclient生产SWRegisterWebpackPlugin,用于向 HTML 中注入 ServiceWorker 注册代码

更多示例

上面列出了最常用的对于 Webpack 的扩展,即修改 Loader 和 插件,其余配置项的使用方法,可以参考 webpack-chain API

另外,我们编写了一个简单的 Codelab,使用 extendWithWebpackChain 方法配合 ESlint 为 Lavas 项目增加代码检查。

compress

该配置项只有 SSR 模式下生效,SPA 模式下可以忽略。

在 SSR 模式下是否启用 gzip,通过内置的 compress 中间件实现。Lavas 默认在开发模式中关闭这一特性,在生产环境打开。

1compress: false

nodeExternalsWhitelist

该配置项只有 SSR 模式下生效,SPA 模式下可以忽略。

在 SSR 模式下,通常我们不希望将 node_modules 中的依赖打包进 server bundle 中,因此需要使用 Webpack externals 配置项。Lavas 已经通过 Webpack node modules externals 将 node_modules 全部排除。但是在某些场景下,我们还是需要将部分特定的依赖打包进来,这时就需要使用白名单了:

1nodeExternalsWhitelist: []

例如在服务端渲染场景下常常遇到的一个问题是,某些第三方依赖使用了 document, window 这样在 Node.js 环境中不存在的对象。为了保证服务端渲染正常运行,通常使用 resolve.alias 引导 Webpack 使用空的 stub 对象,此时一定要同时在 nodeExternalsWhitelist 中加入该依赖。

ssrCopy

该配置项只有 SSR 模式下生效,SPA 模式下可以忽略。

在 SSR 模式下,Lavas 除了将构建产物输出到例如 dist 文件夹中,还可以将例如 node_modules,线上脚本等文件拷贝到里面。这样 dist 文件夹可以作为一个可单独运行的包,移动到任意位置:

1234567891011ssrCopy: isDev ? [] : [ { src: 'server.prod.js' }, { src: 'node_modules' }, { src: 'package.json' } ]

虽然从功能上看都是拷贝文件,但是这些文件并不会经过 CopyWebpackPlugin 处理,这一点不同于 /static 文件夹。

watch

Lavas 在开发模式下使用了 webpack-dev-middleware。得益于自带的热加载功能,很多源文件的修改会自动触发 Webpack 重新编译,不需要开发者重启开发服务器。Lavas 扩充了这个功能,修改以下文件,也会触发重新编译:

  • /pages 下增加删除修改路由组件。

整个重新编译过程中不需要开发者关闭重启服务,也就是说从 MPA 模式切换到 SSR 模式也只需要修改配置后等待编译完成。另外,如果想监控自定义文件、文件夹,在它们发生修改时也触发重新编译,可以通过 watch 配置项传入文件列表:

123watch: [ '/foo/bar' // 自定义文件 ]

development & production

对于以上配置项,如果需要在开发模式和生产环境启用不同的值,可以使用两个特殊的配置项 developmentproduction。 例如想在开发模式关闭 cssExtract 分离样式而在生产环境开启,可以这么做:

12345678910111213build: { // 省略其他配置项 }, development: { build: { cssExtract: false } }, production: { build: { cssExtract: true } }