本文首发于我的博客
前言
慢慢地也用vue2+webpack2重构了一整个项目,期间自己踩过的坑和思考记录下来,既让自己复盘一遍也方便以后查找相关的问题
项目构建
因为vue-cli实在是太友好了,并且也是打算做单页应用,所以直接用vue-cli构建基于webpack2的模板。
整体技术栈使用vue2
+webpack2
+vuex
+Axios
+vue-router
项目总体结构↓
|-- build // 项目构建webpack相关代码
|-- config // 项目开发环境配置
|-- node-moudles //node依赖包(自动生成的)
|-- src // 源码目录(此文件内基本就是开发者主要编写代码存放资源的地方)
|--router //路由文件
|--vuex //vuex文件
|--style //公共样式文件
|--assets //静态资源(大多都是放一些样式类文件,如css、scss等,不知道为啥,当时我没有用)
|--components //组件文件
|-- static // 静态文件,比如一些图片,json数据等(打包成生产环境时,会将这个文件直接打包进去)
|-- .babelrc // ES6语法编译配置
|-- .editorconfig // 定义代码格式
|-- .gitignore // git上传需要忽略的文件格式
|-- README.md // 项目说明
|-- favicon.ico // 页面标签显示的logo
|-- index.html // 入口页面
|-- package.json // 项目基本信息
build文件夹
|-- build // 项目构建webpack相关代码
|--build.js //生产环境构建
|--check-version.js //版本检查
|--dev-client.js //开发服务器热重载
|--dev-server.js //构建本地服务器
|--utils //构建相关工具
|--vue-loader.conf.js //CSS加载器配置
|--webpack.base.conf.js //webpack基础配置
|--webpack.dev.conf.js //webpack开发环境配置
|--webpack.prod.conf.js //webpack生产环境配置
|--webpack.test.conf.js //webpack测试环境配置
build主要是用于webpack的一些相关配置,比如内部含有的启动文件dev-server.js
.当我们运行npm run dev
命令时node就会根据
package.json
中的"dev": "node build/dev-server.js",
来寻找此文件,这些都是默认设置。无需要修改
"scripts": {
"dev": "node build/dev-server.js",
"start": "node build/dev-server.js",
"build": "node build/build.js",
"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
"e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e"
},
config文件夹
config主要就是项目的相关配置,比如自定义修改监听端口,打包输出的路径及其输出文件的命名
|-- config // 项目构建webpack相关代码
|--dev.env.js //项目开发环境配置
|--index.js //项目主要的配置(监听端口,打包路径啥的都在这里写)
|--prod.env.js //项目生产环境配置
|--test.env.js //项目测试环境配置
上述三个文件做简单介绍是因为最后我们在打包优化主要是对于webpack的配置文件进行修改,所以先做简单介绍。
编码过程问题总结
路由标签自定义
用到vue2框架的时候不可避免会用到路由插件,一般配合vue2的官方路由插件是vue-router2
其中有个路由跳转标签为router-link
例子如下
<router-link to="/foo">foo</router-link>
<!-- 渲染结果 -->
<a href="/foo">foo</a>
但是由于样式的原因。有时候我们不希望他被渲染成<a href=""></a>
那我们可以利用router-link
中的tag
属性改变渲染过后的标签
例子如下
<router-link to="/foo" tag="li">foo</router-link>
<!-- 渲染结果 -->
<li>foo</li>
自定义路径别名(@
,~
)
在vue2过程导入组件或者css时可能会有这种语法
import Index from '@/components/Index'
这种@
是什么东西?
在配置文件中这是webpack的配置项之一:路径别名
我们可以在基础配置文件中自定义自己的路径别名,比如设置~
为src/components
:
// build/webpack.base.js
{
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'~': resolve('src/components')
}
}
}
然后导入组件就可以直接这样写
//import YourComponent from 'src/components/YourComponent
import YourComponent from '~/YourComponent'
这样就解决了路径过长与相对路径的烦恼,方便多了吧
CSS作用域与模块
在组件化开发过程中本着分离的原则,CSS也应该进行独立分离,这样更方便以后维护。
我们可以通过添加scoped
属性来使style中的样式只作用于当前组件
<style lang="less" scoped>
@import 'other.less';
.main{
color:#fff
}
</style>
注意:
在有scoped属性的style标签内导入其他样式,同样会被限制于该作用域,变为组件内的样式。这样再每次打包之后会大大增加CSS体积,所以复用程度较高的样式不建议这样使用
,第一次我就出了这个问题
另外,在组件内样式中应该避免使用元素选择器,原因在于元素选择器与属性选择器进行查询时性能会大大降低
好像这条不止于在vue中,而是css本来就存在的
v-for的使用
v-for
指令真是无比便捷,他让我们大大减少了HTML代码的编码量,并且让模板更加与数据契合。
不仅可以遍历数组,对象,甚至可以遍历字符串与数字
我们经常会遇到一些比如,上下移动行啊什么的需求。这些需求基本都绕不过索引
在使用v-for时候我们可以直接
<li v-for="(item,index) of items" :index="index" :key="index"></li>
这样直接可以利用li标签的index属性就可以拿到当前这行的index。tr同理
但是在便利数字时候需要特别注意,数字是从1开始的
项目路径配置
vue-cli
提供的 npm run build
打包出的dist文件是默认把index.html当做根目录引用的,然而我们有时候是需要部署到服务中的子目录下
那么我们可以修改config/index.js
中来改变相对路径:
build.assetsSubDirectory: 'static'
build.assetsPublicPath: '/'
dev.assetsSubDirectory: 'static'
dev.assetsPublicPath: '/'
assetsSubDirectory
指的是静态资源文件夹,也就是打包后的js、css、img等文件所放置的文件夹。这个一般都不需要修改
assetsPublicPath
而这个属性就是指index中引用静态资源的路径,默认为/
。如果我们需要做特殊配置修改这里的路径就好。路径修改为最后你的index.html
到这个静态资源的相对路径
开发环境配置
何为开发环境:在开发时,不可避免会产生大量debug又或是测试的代码,这些代码不应出现在生产环境中(也即不应提供给用户)。
开启webpack的cache
在webpack.base.conf.js
中,在module.exports
内加入cache: true
这个属性
配置加载器loader中的include与exclude
加载器loader
中的include
属性代表需要编译的文件,而反之exclude
就是不需要编译的文件。
大多数情况下我们几乎都不需要对node_modules
下的js
文件进行编译,我们只需要对我们项目目录下自己编写的js
进行编辑就好,所以我们通过配置这两个选项,大大提升我们的构建速度
在webpack.base.conf.js
中
// ... 其他配置
module: {
rules: [
{
test: /\.js$/,
loader: ['babel-loader?cacheDirectory=true'],
include: [resolve('src')], // src是项目开发的目录
exclude: [path.resolve('../../node_modules')] // 不需要编译node_modules下的js
},
// ... 其他loader
]
}
开启babel-loader插件的cache
开启babel-loader
的编译缓存,以后编译就只会编译修改过的地方,重用的地方就不会重新编译,大大节约构建速度。
此代码在webpack.base.conf.js
中
module:{
rules: [
{
test: /\.js$/,
loader: ['babel-loader?cacheDirectory=true'] //修改这里即可
},
]
}
使用HappyPack加快编译速度
虽然我们有webpack的热更新,但每次初始化启动项目时发现总是需要等待很长的时间才能完成编译。
那么我们就可以利用HappyPack插件开启多线程
,大大加快代码构建速度。
主要核心技术有两点:
- 利用Loader的并发可行性完成多线程
- 在每次编译时候都会针对对应文件存入一个时间戳并且将编译结果缓存 ,下次编译时,就会先读取这个时间戳。利用这个做key去对比,如果没有变那么就直接读取缓存。
例子代码如下:
//先引入happypack与相关模块
let HappyPack = require('happypack');
let os = require('os');
let happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module:{
rules: [{
test: /\.vue$/,
loader: 'vue-loader',
// options: vueLoaderConfig
options: {
loaders: {
js: 'happypack/loader?id=js' // 将loader换成happypack
}
}
},
{
test: /\.js$/,
// loader: 'babel-loader',
// loader: ['babel-loader?cacheDirectory=true'],
loader: ['happypack/loader?id=js'], // 将loader换成happypack
include: [resolve('src'), resolve('test')],
exclude: [path.resolve('../../node_modules')] // 不需要编译node_modules下的js
},
...
}
plugins:[
new HappyPack({
id: 'vue',
threadPool: happyThreadPool,
cache: true,
verbose: true,
loaders: ['vue-loader'],
}),
new HappyPack({
id: 'js',
threadPool: happyThreadPool,
cache: true,
verbose: true,
loaders: ['babel-loader'],
}),
]
生产环境配置
何为生产环境配置:页面部署到服务器时,为了追求性能加大用户体验,所以我们会对代码进行各种各样的优化,比如说混淆、压缩,这些手段往往会彻底破坏代码本身的可读性,不利于我们进行debug等工作。
利用CommonsChuncksPlugin解决重复引用
我们在开发过程中为了方便代码维护,可能会抽出一些公关的模块,这里不止于vue
的共用组件,如果用了css预编译器
,那更是会有@mixin
这种宏(scss是这样,其他可能不是这个名词但是也会有)。
那么在打包过程中将会把引入的模块重复打包
,这样大大增加了最后的文件体积,这样是肯定不可取的。
而webpack有个插件叫CommonsChuncksPlugin
。
就是专门把重复打包的模块给抽取出来单独打包的插件。这个能够显著降低最后打包的体积,也能提升一些打包速度。
使用也很方便,就是在webpack.base.conf.js
中的plugins
添加相关的配置代码,如下
plugins: [
new webpack.optimize.CommonsChunkPlugin({
async: 'shared-module',
minChunks: (module, count) => (
count >= 2 // 当一个模块被重复引用2次或以上的时候单独打包起来。
)
}),
//...
]