前言
为什么要使用模块化进行开发?
- 避免全局变量命名污染
- 代码复用
- 将项目功能分块开发,易于团队合作,便于后期维护修改
在 ES6 之前,我们要想进行模块化开发,就必须借助于其他的工具。
webpack 可以让我们进模块化开发,并且会帮助我们处理模块间的各种依赖,并且将其进行整合打包。
在打包的过程中,还可以对资源进行处理,比如压缩图片,将 scss 转换成 css,将 ES6 语法转换成 ES5 语法,将 TypeScript 转成 JavaScript 等等操作。
安装
webpack 依赖于 Node.js,所以需要先安装 Node.js
安装 webpack
npm install webpack@3.6.0 —save-dev
全局安装 webpack
npm install webpack@3.6.0 -g
webpack 安装 3.6.0 版本,因为vue cli2依赖该版本,vue cli2 能看到 webpack 的配置文件,而 vue cli3 看不到
使用 webpack 命令进行打包
webpack [打包路径] [输出路径]
配置
可以配置 webpack.config.js 文件来实现自动打包,详细可以看官方文档
// webpack.config.js
// 动态获取路径需要用来的模块
const path = require('path');
module.exports = {
// 入口
entry: './src/main.js',
// 出口
output: {
// 出口路径
// 动态获取绝对路径
path: path.resolve(__dirname, 'dist'),
// 打包生成文件的名称
filename: 'bundle.js'
},
}
通常我们使用 package.json 中的脚本来进行打包
将 build 映射到 webpakc 命令
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
}
现在就使用 npm run build 进行打包了
在 package.json 中定义的脚本会优先在本地查找运行
loader
webpack 可以使用 loader 来预处理文件。这允许你打包除 JavaScript 之外的任何静态资源。
注意:使用多个 loader 时,加载顺序从右至左
**
css-loader
解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码(只负责加载)
安装
npm install —save-dev css-loader
配置
// webpack.config.js
module.exports = {
module: {
rules: [
{
// 匹配规则,匹配 .css 为扩展名的文件
test: /\.css$/,
// style-loader 负责将样式添加到 DOM 中
use: [ 'style-loader', 'css-loader' ]
}
]
}
}
style-loader
_
将模块的导出作为样式添加到 DOM 中
安装
npm install style-loader —save-dev
配置
// webpack.config.js
{
module: {
rules: [
{
test: /\.css$/,
use: [
// css-loader 负责将 css 文件进行加载
{ loader: "style-loader" },
{ loader: "css-loader" }
]
}
]
}
}
less-loader
加载和转译 LESS 文件
安装
npm install —save-dev less-loader less
less-loader 需要 less依赖,所以同时需要安装 less
配置
// webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /\.less$/,
// 有顺序要求
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // 将 CSS 转换为 CommonJS(将 CSS 导入到 js 文件中)
}, {
loader: "less-loader" // creates style nodes from JS strings
}]
}]
}
};
url-loader
像 file loader 一样工作,但如果文件小于限制,可以返回 data URL(base64)
安装
npm install —save-dev url-loader
配置
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
// 限制图片大小(单位 byte),小于这个大小的图片会转为 base64
// 当加载的图片大于 limit 时,需要使用 file-loader 模块进行加载
// file-loader 不需要配置
// 当大于 limit 时 file-loader 会自动将图片打包到出口(output)路径
limit: 8192,
// 对生成的文件进行命名规范
// 文件夹/图片原本的名称.截取 hash 值为 8 位.原本的扩展名
name: 'img/[name].[hash:8].[ext]'
}
}
]
}
]
}
}
publicPath 配置
module.exports = {
output: {
// 只要涉及到 url(静态文件),会自动在路径前面加上 publicPath
publicPath: './dist/'
}
}
babel-loader
这个 loader 可以对 ES6 语法处理,转换为 ES5
安装
npm install —save-dev babel-loader@7 babel-core babel-preset-es2015
babel-core 是 babel-loader 的依赖,babel-preset-es2015 则是转换成 ES5 需要安装的,如果是转换成 TypeScript 则需要安装其他
配置
// webpack.config.js
module: {
rules: [
{
// 匹配 .js 文件
test: /\.js$/,
// 排除不需要转换的文件
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
]
}
配置 Vue
安装 Vue
npm install —save vue
配置 Vue
// webpack.config.js
// vue 有两个版本
// 1.runtime-only 代码中不可以有任何的 template
// 2.runtime-compiler 代码中可以有 template
resolve: {
alias: {
// 指定使用 node_modules 中 Vue 的文件
// 默认是使用 vue.runtime.js
'vue$': 'vue/dist/vue.esm.js'
}
}
安装 loader
npm install vue-loader vue-template-compiler —save-dev
vue-loader 用于加载 .vue 文件,vue-template-compiler 用于编译 .vue 文件(两个 loader 的版本需要一致)
配置 loader
// webpack.config.js
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
}
]
}
Vue Loader v15 现在需要配合一个 webpack 插件才能正确使用:
// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
// ...
plugins: [
new VueLoaderPlugin()
]
}
配置完后就能使用 .vue 的文件了
示 例
// main.js
// 加载 Vue
import Vue from 'vue';
// 将模版和数据提取到 .vue 文件并分离(模板和数据进行了分离)
import App from './vue/app.vue'
new Vue({
el: '#app',
// template 会把 html 中的 <div id="app"><div> 替换掉
template: '<App/>',
components: {
App
}
})
// app.vue
<template>
<div>
<h2 class="title">{{ message }}</h2>
<button>按钮</button>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
message: "Hello webpack",
};
},
};
</script>
<style>
.title {
color: green;
}
</style>
plugin
BannerPlugin
为打包的文件添加版权声名
_
配置
// webpack.config.js
// 使用 webpack 内置的 plugin 需要导入 webpack
const webpack = require('webpack')
module.exports = {
plugins: [
new webpack.BannerPlugin('最终版权归xxx所有')
]
}
html-webpack-plugin
将 index.html 打包到 dist 文件夹中,并且会自动引入打包的 js 文件
_
安装
npm install —save-dev html-webpack-plugin
配置
// webpack.config.js
// 导入 html-webpakc-plugin 插件
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
new htmlWebpackPlugin({
// 指定模版生成 index.html
template: 'index.html'
})
]
}
由于 index.html 会自动打包到 dist 中,module.exports.publicPath 就是多余的了
uglifyjs-webpack-plugin
压缩 js 文件(压缩后会删除注释,开发中时并不适合压缩文件,调试时候不方便)
安装_
npm install —save-dev uglifyjs-webpack-plugin@1.1.1
版本号指定 1.1.1,和 CLI2 保持一致(官方没有使用 webpack 自带的压缩插件)
_
配置
// webpack.config.js
// 导入 uglifyjs-webpack-plugin 插件
const uglifyjsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
plugins: [
new uglifyjsPlugin()
]
}
webpack-dev-serve
开启本地服务器
_
安装
npm install —save-dev webpack-dev-server@2.9.3
将开启服务命令映射到 package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
// --open 执行并打开
"dev": "webpack-dev-server --open"
}
配置
// webpack.config.js
module.exports = {
devServer: {
// 需要服务的文件夹
contentBase: './dist',
// 是否实时进行监听
inline: true,
// 默认运行 8080 端口
// port:端口号
}
}
配置分离
像一些 uglifyjs-webpack-plugin
脚本在开发时并不会使用,而 webpack-dev-serve
脚本却只在生产环境时会使用到,所以我们可以让开发时和生产时的环境配置分离。
配置文件分离后合并需要用到 webpack-merge
插件
安装 webpack-merge
npm install webpack-merge —save-dev
新建一个 build 文件夹,并创建 base.config.js、dev.config.js 和 prod.config.js 文件
分离开发时配置文件
// dev.config.js
// 导入合并配置文件要用的插件
const webpackMerge = require('webpack-merge');
// 导入需要合并的配置文件
const baseConfig = require('./base.config');
module.exports = webpackMerge(baseConfig, {
// 只在开发阶段使用
devServer: {
contentBase: './dist',
inline: true,
// 默认运行 8080 端口
// port:端口号
}
})
当文件路径发生改变时需要在 package.json 中指定运行路径
// package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open --config ./build/dev.config.js"
}
分离生产时配置文件
// prod.config.js
// 导入 uglifyjs-webpack-plugin 插件
const uglifyjsPlugin = require('uglifyjs-webpack-plugin');
// 导入合并配置文件要用来的插件
const webpackMerge = require('webpack-merge');
// 导入需要合并的配置文件
const baseConfig = require('./base.config');
module.exports = webpackMerge(baseConfig, {
plugins: [
new uglifyjsPlugin()
]
})
当文件路径发生改变时需要在 package.json 中指定运行路径
// package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config ./build/prod.config.js"
}
文件别名
给文件起别名可以更好地查找文件
用 import 加载文件时,可以在路径中正常使用别名,但在 src 中时,需要在别名前加 ~
符号
module.exports = {
resolve: {
alias: {
// @ 会自动查找 src 的路径
// @/img 相当于 src 下的 img
'@': resolve('src'),
}
},
}
Vue CLI2
使用
安装 Vue 脚手架
npm install -g @vue/cli
上面安装的 Vue CLI3 的版本,如果需要想按照 Vue CLI2 的方式初始化项目时需要安装一个格拉工具
拉取 2.x 模板(旧版本) npm install -g @vue/cli-init
Vue CLI2 初始化项目
vue init webpack projectName
目录结构
│ .babelrc
│ .editorconfig
│ .eslintignore
│ .eslintrc.js
│ .gitignore
│ .postcssrc.js
│ filedir.txt
│ index.html
│ package-lock.json
│ package.json
│ README.md
│
├─build
│ build.js
│ check-versions.js
│ logo.png
│ utils.js
│ vue-loader.conf.js
│ webpack.base.conf.js
│ webpack.dev.conf.js
│ webpack.prod.conf.js
│
├─config
│ dev.env.js
│ index.js
│ prod.env.js
│
├─src
│ │ App.vue
│ │ main.js
│ │
│ ├─assets
│ │ logo.png
│ │
│ └─components
│ HelloWorld.vue
│
└─static
.gitkeep
package.json
- build 脚本
node build/build.js 运行 build 下的 build.js 文件
build.js 中 引入了 webpack.prod.conf 生产时环境配置
webpack.prod.conf 中合并了基础环境配置 webpack.base.conf.js
- dev 脚本
webpack-dev-server —inline —progress —config build/webpack.dev.conf.js
基于 webpack-dev-sever 插件开启本地服务器,并指定了配置文件为 build/webpack.dev.conf.js
build/webpack.dev.conf.js 中合并了基础环境配置 webpack.base.conf.js
config
index.js 定义了配置文件中用到的变量
src
开发时文件夹
static
存放静态文件(发布时会原封不动复制到 dist 文件夹中)
.editorconfig
项目文本相关配置
# 当 root 为 true 时才会解析该文件
root = true
[*]
charset = utf-8
# 缩进风格
indent_style = space
# 缩进的大小为 2 个空格
indent_size = 2
# 在行尾换行
end_of_line = lf
# 在最后插入一个新的行
insert_final_newline = true
# 清除无效的空格
trim_trailing_whitespace = true
.eslintignore
**
忽略 eslint 检测的配置文件
编译模式
一、问题
用 vue cli2 创建项目时,会让我们选择 runtime-compiler 还是 runtime-only
二、结论
- runtime-only 比 runtime-compiler 轻 6kb
- runtime-only 运行更快
- runtime-only 只能识别 render 函数,不能识别 template,.vue 文件通过 vue-template-compiler 编译成 render 函数,所以只能在 .vue 文件里写 template
三、解释
**
runtim-compiler 的运行过程
template -> ast -> render -> virtual dom -> 真实 dom
runtim-only 的运行过程
render -> virtual dom -> 真实 dom
由于 runtim-only 比 runtim-compiler 少的两个过程,所以性能更高、源代码量更少(小 6kb)
两种模式生成的代码区别只在 main.js 中:
h => h(App) 中的 h 实际上是 createElement 函数,用于创建虚拟 dom
createElement 函数的语法:
createElement(‘标签’, { 标签的属性 }, [ ‘标签的内容’ ]) createElement(组件对象)
示例1
编译前:
// 传入标签的用法
new Vue({
el: '#app',
// render: h => h(App)
render: function (createElement) {
return createElement('h2', { class: 'box' }, ['Hello World'])
}
})
// createElement 的第二个参数是可以省略的, 并且可以添加子元素
return createElement('h2',
{ class: 'box' },
['Hello World', createElement('button', ['按钮'])])
编译后:
<!-- index.html -->
<!-- 替换了原本的 <div id="app"></div> -->
<h2 class="box">Hello World</h2>
<h2 class="box">Hello World
<button>按钮</button>
</h2>
示例2
编译前
const cpn = {
template: `<div>{{message}}</div>`,
data() {
return {
message: '我是组件的 message'
}
}
new Vue({
el: '#app',
// render: h => h(App)
render: function (createElement) {
return createElement(cpn)
}
})
编译后
<!-- index.html -->
<!-- 替换了原本的 <div id="app"></div> -->
<div>我是组件的 message</div>
所以可以将 runtim-only 源码中的 App 作为模板传入了 createElement() 中
虽然 App.vue 文件中依然是有 template
但是当 vue 编译后,template 会渲染成 render 函数(由 vue-template-compiler 此 loader 编译),成为一个对象
build 和 dev
npm run build 运行过程
npm run dev 运行过程
打包文件解析
Vue CLI3
与 vue-cli 2 的区别
- vue-cli 3 是基于 webpack 4 打造,vue-cli2 还是 webpack 3
- vue-cli 3 的设计原则是”0配置”, 移除了配置文件根目录下的, build 和 config 等目录
- vue-cli 3 提供了 vue ui 命令,提供了可视化配置,更加人性化
- 移除了 static 文件夹,新增了 public 文件夹,并且 index.html 移动到 public中
使用
Vue CLI3 初始化项目
vue create projectName
目录结构
配置文件
- vue ui
使用 vue ui
命令启动可视化本地项目管理器服务(任何目录下执行都可以)
导入或创建项目进行配置管理
- 自定义配置
在项目根目录下创建 vue.config.js,然后创建对应相关的配置,最终会和其它配置合并
babelrc 和 babel.config.js
- babelrc 只会影响本项目中的代码,
- babel.config.js 会影响整个项目中的代码,包含 node_modules中的代码
- babel.config.js 是按照 commonjs 导出对象,可以写 js 的逻辑
- 一般有了babel.config.js 就不会在去执行.babelrc的设置