为了让浏览器能够正确的解析, 我们需要使用 webpack 将我们的源代码进行打包
#1 创建 webpack 配置文件
在根目录下创建 webpack 配置文件 webpack.config.js, 编写最基本的配置
示例
// 使用node的path模块
const path = require('path')
module.exports = {
// 打包的入口
entry: './src/main.js',
// 打包的出口
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
}
- entry: 指定打包的入口
- output: 指定打包的出口, 也就是最终生成的文件
演示
#2 编写 webpack 脚本
在 packpage.json 中, 创建一个执行脚本
这样就可以通过npm run build
来执行 webpack 打包了
#3 测试
在命令行中执行npm run build
会发现提示这个错误
原因是: webpack 只能打包 js 文件. 对于后缀名为 vue 的文件不能打包. 如何解决呢?
通过 vue-loader 来打包 vue 文件
==============================================
打包vue文件
找到 vue-loader 官网(opens new window)
对于最新的 vue-loader@15 版本, 按照官网的指导, 做如下配置
#1 安装 vue-loader
执行命令, 安装 vue-loader
npm install -D vue-loader vue-template-compiler
安装完成后, 发现 vue-loader 依赖 css-loader, 因此我们也要手动安装一下 css-loader
#2 安装 css-loader
执行npm install -D css-loader
#3 webpack 配置
从 vue-load@15 版本开始, vue-loader 需要在 webpack 中添加一个插件
示例
// 使用node的path模块
const path = require('path')
// 引入vue-loader插件
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
// 打包的入口
entry: './src/main.js',
// 打包的出口
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
// 打包规则
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
],
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin(),
],
}
演示
#4 重新打包测试
重新打包, 在 dist 目录下就会生成 bundle.js
解决警告小问题
我们发现在 webpack 打包时会出现这个警告
解决
找到 webpack.config.js 在配置中添加 mode
#5 在主页中引入 bundle.js
在 index.html 中引入 bundle.js 测试, 发现报错
原因
Vue 会打包生成三个文件:
- runtime only 的文件 vue.common.js
- compiler only 的文件 compiler.js
- runtime + compiler 的文件 vue.js
而默认导出的是 vue.common.js, 如何解决呢?
解决
在 webpack 中, 添加别名的配置
resolve: {
alias: {
'vue': 'vue/dist/vue.js'
}
},
演示
重新打包, 测试
#6 小结
通过上面的练习, 我们知道
- webpack 本身只能打包 js 文件, 如果要打包其他文件就需要借助于 loader
- loader 其实就是专门用于打包特定文件的处理程序
=====================================
其他常用loader
一般来说, 一个前端项目除了 js 文件外, 还有一些常用的文件, 如
- 图片文件
- css 文件
对于这些文件, webpack 都不会打包, 需要我们安装对应的 loader 帮助 webpack 打包
#1 打包图片文件
#1) file-loader
file-loader: 将文件复制到对应的路径, 并返回文件名
file-loader官方文档(opens new window)
#i 安装 file-loader
执行命令, 安装 file-loader
npm install -D file-loader
演示
#ii 配置
修改 webpack 配置, 添加一条规则
module: {
rules: [
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'file-loader',
},
]
}
- test: 正则表达式--如果需要打包的文件以 jpg 或者 jpeg 或者 png 或者 svg 结尾时
- loader: 使用 file-loader
演示
#iii 测试
在 src 目录下创建 assets 目录, 存放静态资源文件(如: images/styles/fonts 等)
在 images 下放一张图片
在 App.vue 中导入图片, 重新打包, 会在 dist 目录下生成我们需要的图片
原理: 当遇到 jpg 结尾的文件时, 使用 file-loader 将文件 copy 到 dist 目录下. 文件名是图片的 hash 值
如果希望保留原有的文件名, 可以使用占位符(placeholder)配置
示例
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
}
[name]
: 占位符, 表示使用原文件名[ext]
: 占位符, 表示使用原文件扩展名
演示
#2) url-loader
url-loader: 功能类似于 file-loader (opens new window),但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。
好处是: 直接将小图片打包以 base64 打包在 js 中, 减少 Http 请求的次数, 提高访问效率
#i 安装 url-loader
执行命令, 安装 url-loader
npm install -D url-loader
提示
由于 url-loader 依赖 file-loader, 因此安装 url-loader 需要先安装 file-loader, 一般来说执行如下命令更适合
npm install -D file-loader url-loader
同时安装 file-loader 和 url-loader
演示
#ii 配置
修改 webpack 配置, 添加一条规则
module: {
rules: [
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'url-loader',
options: {
name: '[name].[ext]',
limit: 2048,
},
},
]
}
- test: 正则表达式--如果需要打包的文件以 jpg 或者 jpeg 或者 png 或者 svg 结尾时
- loader: 使用 url-loader
- options: 选项
- limit: 当文件小于 2048K 时, 以 base64 打包到 js 中, 当文件大于 2048K 时, 使用 file-loader 打包
- name: 打包的文件名使用"源文件名.扩展名"方式
#iii 测试
复制两张小的 svg 图片到 assets 目录下. 并在 App.vue 中导入
运行打包命令npm run build
, 在新生成的 bundle.js 中, 可以看到
#2 打包 css 文件
css 文件是前端项目必不可少的文件, webpack 通过 css-loader 和 style-loader 来打包 css 文件
#1) 安装
#i 安装 css-loader
在安装 vue-loader 时, 我们已经安装了 css-loader. 可以通过查看package.json
确认
#ii 安装 style-loader
在命令行中执行如下命令
npm install -D style-loader
演示
#2) 配置
示例
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}]
},
- test: 正则(以 css 结尾的文件)
- use: 使用两个 loader: style-loader 和 css-loader
注意
这里的书写顺序是有讲究的, 按照从右到左, 从下到上的顺序依次执行
#3) 测试
在 styles 目录下创建 test.css 文件, 编写如下内容
示例
* {
margin: 0;
padding: 0;
}
body {
background-color: red;
}
演示
在 App.vue 中导入 test.css
执行命令npm run build
重新打包, 测试, 发现生效
#4) 作用
css-loader: 解决文件之间的依赖关系, 把所有的 css 文件打包成一个文件
style-loader: 将 css-loader 打包完成后生成的文件挂载到页面的 head 标签的 style 中
示例
比如, 在实际项目中一般会把初始化样式独立出来, 再通过 import 引入
css-loader 为分析 css 文件之间的引用关系, 最终形成一个整体文件
再通过 style-loader 将 css-loader 的内容挂载到页面的 head 的 style 标签中
#3 打包 stylus 文件
目前, stylus 做为 node 项目普通使用的 css 预处理器被广泛的应用于 vue 项目中. 大家会发现大部分的 vue 项目中都会使用 stylus 来编写 css.
#1) 推荐的 vscode 插件与配置
- language-stylus: 提供语法高亮效果和一些支持
- Supremacy: 自动格式化 stylus 的插件, 可以根据个人习惯或公司的要求, 定制 stylus 格式. 比如是否要
;
,:
这里我对 Supermacy 的配置如下,
不使用{}, 不使用结尾的;
, 但是保留:
配置之后, stylus 的样子如下:
#2) 打包 stylus 文件
#i 安装 stylus-loader
执行命令
npm install -D stylus stylus-loader
- stylus: 是 stylus 文件预处理程序, 作用是将 stylus 编译成 css 格式
- stylus-loader: 加载 stylus 文件, 调用 stylus 预处理程序形成 css 文件
#ii 配置
示例
module: {
rules: [{
test: /\.styl(us)?$/,
use: ['style-loader', 'css-loader', 'stylus-loader']
}]
},
- test: 正则(匹配以 styl 结尾或者 stylus 结尾的文件)
- use: 依次使用 stylus-loader, css-loader, style-loader 处理
#iii 测试
在 styles 下编写 global.styl 文件, 编写如下内容, 这个也是响应式背景全屏的方案
示例
body
// 背景图片
background-image: url('..//http://image.brojie.cn/bg.jpg')
// 背景图片位置固定
background-attachment: fixed
// 背景不要重复
background-repeat: no-repeat
// 背景位置居中
background-position: center center
// 背景覆盖整个viewport
background-size: cover
// 当背景没有加载时的颜色
background-color: #fff
演示
在 App.vue 中引入 global.styl
示例
import './assets/styles/global.styl'
演示
由于在开发环境, 打包后的文件路径都是相对于 dist 目录, 因此我们还需要暂时把 index.html 移动到 dist 下, 并做如下修改
打包以后测试如下
#3) 处理 vue 文件中的 stylus
在 vue-loader 的官方文档中找到关于 stylus 的配置(opens new window)
#i 安装
在前面, 我们已经安装了 stylus 和 stylus-loader, 这里就不用再安装了
#ii 配置
修改 webpack.config.js
示例
module: {
rules: [{
test: /\.styl(us)?$/,
use: ['vue-style-loader', 'css-loader', 'stylus-loader']
}]
},
- vue-style-loader: 是 vue-loader 自带的 style-loader, 在 style-loader 的基础上, 还可以处理 vue 文件中的样式
#iii 测试
我们在 App.vue 中修改一下
打包测试
===============================================
插件
什么是插件
在某个时间点, 自动执行的处理程序(类似于 vue 的生命周期函数)
#1 使用 html-webpack-plugin 插件
查看该插件的官方文档(opens new window)
#1) 安装
执行命令
npm install -D html-webpack-plugin
#2) 配置
修改 webpack.config.js
#3) 打包测试
在 dist 目录下, 生成了 index.html, 并且自动引入了打包后的 js 文件
这时, 我们直接访问发现只有背景, 没有 App 的内容, 并且报了一个 vue 的错误
我们发现自动生成的 html 中没有 id 为 app 的 div 元素, 如何解决这个问题?
#4) 指定模板
其实, 我们是可以指定生成 html 时, 以哪个文件为模板的
修改 webpack 配置
示例
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
],
演示
#5) 小结
html-webpack-plugin 的作用:
在打包结束时, 在 dist 目录下自动生成 index.html 文件, 并把打包好的 js 文件引入到 html 中
#2 使用 clean-webpack-plugin
#1) 安装
执行命令
npm install -D clean-webpack-plugin
1
#2) 配置
webpack 的相关配置
第 10 行: 加{}是 ES6 中的解构的语法, 作用是提取出 CleanWebpackPlugin 的构造函数
#3) 测试
#3 使用 autoprefixer 插件
#1) 安装
autoprefixer 插件是 postcss-loader 提供的一个插件, 如果要使用这个插件, 我们得先安装 postcss-loader
npm install -D postcss-loader autoprefixer
#2) 配置
webpack 配置
新建 postcss.config.js 配置文件
示例
module.exports = {
plugins: [require('autoprefixer')],
}
演示
#3) 测试
测试发现, 会自动添加厂商前缀
==============================
开发环境
#1 devServer
webpack-dev-server
为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。
#1) 配置 devServer
查看该部分的相关文档(opens new window)
#i 安装
执行命令
npm install -D webpack-dev-server
#ii 基本配置
示例
webpack 配置
// devServer配置
devServer: {
// 指定服务器根目录
contentBase: './dist',
// 自动打开浏览器
open: true
},
在 package.json 中添加一个脚本
示例
"scripts": {
"start": "webpack-dev-server"
},
在命令行中执行npm run start
启动 devServer
#2) 测试
测试发现, 只要代码修改后, webpack 会自动重新打包, 页面会重新刷新加载. 这样就不需要每次执行打包命令了
#3) 其他常见配置
查看该部分的相关文档(opens new window)
- host: 服务器主机
- port: 端口
- open: 打开浏览器
- hot: 热模块替换
- proxy: 代理
#2 热模块替换
查看该部分的相关文档(opens new window)
#1) 启用
#i 配置
示例
devServer: {
// 指定服务器根目录
contentBase: './dist',
// 自动打开浏览器
open: true,
// 启用热模块替换
hot: true
},
#ii 插件
#2) 测试
编写业务代码 App.vue
示例
<template>
<div>
<h1>TodoList</h1>
<input type="text" v-model="content" />
<button @click="addTodo">添加</button>
<ul>
<li v-for="item of todoData" :key="item.id">{{ item }}</li>
</ul>
</div>
</template>
<script>
import './assets/styles/global.styl'
export default {
name: 'App',
data() {
return {
todoData: ['todo1', 'todo2', 'todo3'],
content: '',
}
},
methods: {
addTodo() {
if (this.content === '') return
this.todoData.push(this.content)
this.content = ''
},
},
}
</script>
<style lang="stylus" scoped>
li:nth-of-type(odd)
color: red
</style>
修改颜色, 发现之前添加的内容依然存在
#3 SourceMap
SourceMap: (源代码映射) 建立打包后的文件和源代码所在行的映射,
主要作用: 在开发时快速定位到出错的源代码行
查看该部分的相关文档(opens new window)
===============
生产环境
开发环境(development) 和 生产环境(production) 的构建目标差异很大。
- 在 开发环境 中,我们需要具有强大的、具有实时重新加载(live reloading)或热模块替换(hot module replacement)能力的 source map 和 localhost server。
- 在 生产环境 中,我们的目标则转向于关注更小的 bundle,更轻量的 source map,以及更优化的资源,以改善加载时间
因此, 配置会有所不同, 官方推荐使用两个不同的配置文件
- webpack.dev.js: 用于开发环境
- webpack.prod.js: 用于生产环境
#1 分别指定两个配置文件
示例
webpack.dev.js
// 使用node的path模块
const path = require('path')
// 引入vue-loader插件
const VueLoaderPlugin = require('vue-loader/lib/plugin')
// 导入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 导入clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// 导入webpack
const webpack = require('webpack')
module.exports = {
// 模式
mode: 'development',
devtool: 'cheap-module-eval-source-map',
// 打包的入口
entry: './src/main.js',
// devServer配置
devServer: {
// 指定服务器根目录
contentBase: './dist',
// 自动打开浏览器
open: true,
// 启用热模块替换
hot: true,
},
// 插件
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: './index.html',
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin(),
],
// 打包的出口
output: {
filename: 'app.js',
path: path.resolve(__dirname, 'dist'),
},
// 打包规则
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'url-loader',
options: {
name: '[name].[ext]',
limit: 2048,
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.styl(us)?$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'stylus-loader'],
},
],
},
resolve: {
alias: {
vue: 'vue/dist/vue.js',
},
},
}
webpack.prod.js
// 使用node的path模块
const path = require('path')
// 引入vue-loader插件
const VueLoaderPlugin = require('vue-loader/lib/plugin')
// 导入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 导入clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// 模式
mode: 'production',
// 打包的入口
entry: './src/main.js',
// 插件
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: './index.html',
}),
new CleanWebpackPlugin(),
],
// 打包的出口
output: {
filename: 'app.js',
path: path.resolve(__dirname, 'dist'),
},
// 打包规则
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'url-loader',
options: {
name: '[name].[ext]',
limit: 2048,
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.styl(us)?$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'stylus-loader'],
},
],
},
resolve: {
alias: {
vue: 'vue/dist/vue.js',
},
},
}
修改 package.json
"scripts": {
"dev": "webpack-dev-server --config ./webpack.dev.js",
"build": "webpack --config ./webpack.prod.js"
},
#2 提取公共部分
使用 webpack-merge 工具
#1) 安装 webpack-merge
执行命令
npm install -D webpack-merge
#2) 创建 build 目录
创建 3 个文件
- webpack.base.js: 公共配置
- webpack.dev.js: 开发环境配置
- webpack.prod.js: 生产环境配置
示例
webpack.base.js
// 使用node的path模块
const path = require('path')
// 引入vue-loader插件
const VueLoaderPlugin = require('vue-loader/lib/plugin')
// 导入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 导入clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// 打包的入口
entry: './src/main.js',
// 插件
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: './index.html',
}),
new CleanWebpackPlugin(),
],
// 打包的出口
output: {
filename: 'app.js',
path: path.resolve(__dirname, '..', '/dist'),
},
// 打包规则
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'url-loader',
options: {
name: '[name].[ext]',
limit: 2048,
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.styl(us)?$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'stylus-loader'],
},
],
},
resolve: {
alias: {
vue: 'vue/dist/vue.js',
},
},
}
webpack.dev.js
// 导入webpack
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
const devConfig = {
// 模式
mode: 'development',
devtool: 'cheap-module-eval-source-map',
devServer: {
// 指定服务器根目录
contentBase: './dist',
// 自动打开浏览器
open: true,
// 启用热模块替换
hot: true,
},
// 插件
plugins: [new webpack.HotModuleReplacementPlugin()],
}
module.exports = merge(baseConfig, devConfig)
webpack.prod.js
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
const prodConfig = {
// 模式
mode: 'production',
}
module.exports = merge(baseConfig, prodConfig)
#3) 改写 package 配置
示例
"scripts": {
"dev": "webpack-dev-server --config ./build/webpack.dev.js",
"build": "webpack --config ./build/webpack.prod.js"
},