webpack 配置起来确实麻烦,这不,之前用刚配好了 vue1 + 的版本,结果在(部分)安卓机上测试,发现存在开启热加载 (dev-server) 的情况下不能识别 vue 语法的问题,试了很多方法,都没能很好的解决,最后索性将 vue 升级到 2+,竟然就能识别了,好吧!
1. 先分享一下 webpack 配置 vue2 + 的一些不同(本人亲测):
(1)dependencies 中的 vue 默认安装 2+,直接运行,会报如下错:[Vue warn]: Failed to mount component: template or render function not defined。
如果 dependencies 中的 vue 选择 ^1.0.26,那么 devDependencies 中对应的 vue-loader 最好选择 ^7.3.0,vue-hot-reload-api 最好选择 ^1.2.0,否则就会报错。
(2) 如果 vue 选择安装 1+,dependencies 中的 vue-router 最好选择 ^0.7.13(默认安装 2+,无法识别 router.map() 这个方法)。
(3) 如果 vue 选择安装 1+,dependencies 中的 vue-validator 最好选择 ^2.0.0(默认安装 2.1.7)。
(4) 如果 vue 选择安装 1+,后面在开启 webpack dev server 的时候,处于同一内网中的安卓手机访问本地设备的输出页面会出现不识别 vue 语法的兼容问题,ios 手机可以正常访问和解析,但是开启别的 server 再来访问并不会出现这种兼容问题,所以为了测试方便,建议 vue 选择安装 2.0 的版本。
(5) 如果 vue 选择安装 2+,vue2.0 有两种构建模式,默认情况下运行构建,但是不能解析单文件的 template 模板,所以要使用独立构建,需要在 alias 中指定 vue$ 的模块别名地址,即
- // 其他解决方案
- resolve: {
- // require时省略的扩展名,遇到.vue结尾的也要去加载
- extensions: ['', '.js', '.vue'],
- // 模块别名地址,方便后续直接引用别名,无须写长长的地址,注意如果后续不能识别该别名,需要先设置root
- root: "../node_modules",
- alias: {
- 'vue$': 'vue/dist/vue.js'
- }
- },
(6) 如果 vue 选择安装 2+,对应的 vue-validator 就必须安装 3+,否则会报错,但是这样一来在开启 webpack dev server 的时候,又会出现安卓手机不识别 vue-validator 的兼容问题,ios 手机可以正常访问和解析,所以为了测试方便,改用其他的基于 vue 的表单验证插件,即 vuerify,需要引入 vue2+、vuerify 和 v-vuerify-next。
(7) 如果 vue 选择安装 2+,涉及到的 loader 尽量升级到最新,否则会报错;最好是将 vue 升级到 2.0.7,对应的 vue-loader 升级到 8.5.4,vue-html-loader 升级到 1.2.3,vue-hot-reload-api 升级到 2.0.6。
(8) 如果 vue 选择安装 2+,对应的 router 要升级到 2+,但是这时之前使用的表单验证插件就会做出问题 (例如:vue-validator-3.0.0-alpha.1 和 vuerify+v-vuerify-next 这两款插件在路由跳转的时候都会报错,尝试了很多方法都无济于事,感觉还是版本匹配的问题),awesome-vue 集合了来自社区贡献的数以千计的插件和库,在这里我找了一些专门针对 vue2 + 的表单验证插件,发现 Vee-Validate 和 Vue-Easy-Validator 这两款插件没有出现之前遇到的问题,而且前者的英文文档写得相当详细,赞之。
2. 不要小瞧版本匹配问题,webpack 自带插件和第三方插件,vue 和里面需要引入的插件,再加上各种模块加载器,有时匹配对了一个,另一个又会出问题,我就是在这里浪费了太多的时间,最后索性跟着版本走,强势拥抱 2+。关于 vue2 + 的语法,官网里写得很详细,这里就不赘述,我还是接着讲 webpack 配置问题吧:
(1) 配置文件里的入口和出口:
- // 入口文件,路径相对于本文件所在的位置,可以写成字符串、数组、对象
- entry: {
- // 以下是单页面的入口路径
- index: path.resolve(__dirname, "../entry/index.js"),
- // 需要被提取为公共模块的群组
- vendors: ["vue", "vue-router", "vue-resource", "vee-validate", "jquery"],
- },
- // 输出配置
- output: {
- // 输出的js文件,路径相对于本文件所在的位置
- path: path.resolve(__dirname, "../output/js/"),
- // 将入口文件中涉及到的同步加载的js文件打包成一个js文件,基于文件的md5生成hash名称的script来防止缓存
- filename: "[name].[hash].js",
- // 异步加载的业务模块,例如按需加载的.vue单文件组件
- chunkFilename: "[id].[name].[chunkHash].js"
- }
这里需要注意的点不少,我主要说两个:
一个是 publicPath,上次说过测试环境里写成 config.output.publicPath = "/",不建议在生产环境里动它,但最近导出文件的时候如果不设置 publicPath,按需加载的. vue 单文件组件中的 script 路径会报错,所以还是得设置一下 publicPath,其路径可以写成相对于生成的 html 单文件所在位置的相对路径;
另一个就是 chunkFilename,上次没怎么提它,主要是没怎么用到它,如果项目里涉及到异步加载的业务模块,就不得不提它了。如果使用 AMD 风格的 requireJS 来实现路由组件的懒加载,例如:
- const Register = resolve = >require(["../src/private/components/register"], resolve);
这样这个组件不会和 entry 中引入的 js 文件一起打包,而是单独打包成一个 js 文件,名字就是这里的 chunkFilename,带一个自动分配的,可读性很差的 [id]。如果想在命名的时候更有归属感,即带上一个 [name],可以使用 require.ensure 来实现路由组件的懒加载,例如:
- const Register = resolve = >require.ensure(["../src/private/components/register"], () = >resolve(require("../src/private/components/register")), "register");
如果要把某个路由下的所有组件都打包在同个异步 chunk 中,无须明确列出 require.ensure 的依赖,即传空数组就行。如果你还想在按需加载某个模块的同时执行一些代码,可以写成:
- const Register = resolve = >{
- require.ensure(["../src/private/components/register"], () = >{
- // 这里可以写异步加载指定模块之前的代码
- resolve(require("../src/private/components/register"));
- // 这里可以写异步加载指定模块之后的代码
- },
- "register")
- }
(2) 在测试环境中写了 config.output.publicPath = "/" 之后,当前配置文件下的很多相对路径都是相对于这个来设定,即很多涉及到相对路径的地方需要发生相应的变化,否则开启 dev-server 之后会报错找不到文件的错误,那么有哪些地方需要改呢,我个人建议改以下几个地方:
某些模块加载器的路径,例如加载图片的 url-loader 和加载图标的 file-loader;
某些插件的路径,例如生成单个 html 文件的 HtmlWebpackPlugin,提取 CSS 单文件的 ExtractTextPlugin
(3) 为了防止 "找不到 favicon.ico 文件" 这种错误带来的干扰,找一张图塞到项目根目录下,输出的时候直接在 new HtmlWebpackPlugin 插件参数列中写 favicon: "favicon.ico",然后开启 dev-server 就不会报错啦,但是生产环境下还是会报错,原因是导出的位置和输出的 js 文件同级,所以得把它重新塞到和输出的 html 文件同级,这里我是用的 CopyWebpackPlugin 这款插件(第三方插件),代码如下:
- // 把指定的文件复制到指定的目录
- new CopyWebpackPlugin([
- // from写的是源文件名,这里的位置是在项目根目录下,to是写将要复制过去的目录位置,相对于输出的js文件
- {
- from: 'favicon.ico',
- to: "../html/favicon.ico"
- }])
(4) 对于. vue 单文件,css 默认是内部样式,现在要把它里面的 css 提取出来变成外部导入,但是如果. vue 单文件组件是按需加载,那么此设置无效,即会重新变回内部样式(也可能是我自己弄错了),代码如下:
- vue: {
- loaders: {
- css: ExtractTextPlugin.extract('vue-style-loader', 'css-loader'),
- }
- }
(5)config.devtool 这个就看自己喜好吧,开发环境下推荐使用 cheap-module-eval-source-map,生产环境下推荐使用 cheap-source-map 或 source-map,后者得到的. map 文件体积比较大,但是能够完全还原以前的 js 代码
(6) 如果要提取入口文件里面的公共模块,配置文件中必须要有以下三步:
- entry: {
- // path.resolve([from ...], to) 将to参数解析为绝对路径
- index: path.resolve(__dirname, '.index.js'),
- // 需要被提取为公共模块的群组
- vendors: ['vue', 'vue-router', 'jquery']
- },
- new webpack.optimize.CommonsChunkPlugin({
- name: 'vendors',
- filename: 'vendors.js',
- }),
第三步是 new HtmlWebpackPlugin 插件参数列里的 chunks 里一定要引入 vendors;
(7) 如果将 css 单独提取出来,配置文件中必须要有以下三步:
- var ExtractTextPlugin = require('extract-text-webpack-plugin');
- // module.loaders里添加
- {
- test: /\.css$/,
- // loader: 'style-loader!css-loader',
- // 将样式抽取出来为独立的文件
- loader: ExtractTextPlugin.extract("style-loader", "css-loader"),
- exclude: /node_modules/
- }
- new ExtractTextPlugin("../css/[name].[contenthash].css")
3. 其他需要注意的地方:
未完待续~
来源: http://www.cnblogs.com/brandonhulala/p/6120078.html