npm install [email protected] -g
(这里先指定3.6.0版本,因为vue cli2依赖该版本,可以进行手动配置,便于学习)npm install [email protected] --save-dev
因为一个项目往往依赖特定的webpack版本,全局的版本可能很这个项目的webpack版本不一致,导出打包出现问题
所以通常一个项目,都有自己局部的webpack
执行局部webpack打包:node_modules/.bin/webpack
在终端进入到项目根目录,输入命令:webpack ./src/main.js ./dist/bundle.js
该命令的意思是将main.js在该项目内所依赖的所有js文件打包到dist目录下的bundle文件
如果报错禁止运行脚本,则需要配置powershell
我们每次使用webpack的命令都需要写上入口和出口作为参数,非常麻烦,所以我们要创建一个webpack配置文件:webpack.config.js,在该文件配制好入口和出口后,下一次只需输入webpack
即可打包
绝对路径需要依赖node的path包,一般只要依赖于node环境的项目,都需要创建package.json文件,该文件是告诉关于项目一些信息以及管理node包,使用npm init
命令创建
如果项目有依赖一些node的包,使用npm install
会根据package.json自动安装所需要的包
// 用于获取绝对路径,需要依赖node的path包,node全局中是包含该包的,所以不需单独建包
const path = require('path')
module.exports = {
// 入口:可以是字符串/数组/对象,这里我们入口只有一个,所以写一个字符串即可
entry: './src/main.js',
// 出口:通常是一个对象,里面至少包含两个重要属性:path和filename
output: {
//__dirname:获取当前文件所在绝对路径
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
}
我们在真正的开发中一般使用npm run build
来构建我们的项目,因为使用webpack
命令时后面可能会跟很长的内容,我们想让这个长命令可以由其他命令代替
webpack映射还有一个好处,我们在任何终端执行webpack的时候都会执行全局webpack,但是我们使用映射的命令会优先执行局部webpack(本地),因为package.json中的scripts的脚本在执行时,会先寻找本地的node_modules/.bin路径中对应的命令,如果没找到,再去全局中寻找
打开package.json文件,在scripts对象内添加"build"属性
"scripts": {
// 此时执行npm run build命令将转换为webpack命令
"build": "webpack"
},
前面演示了如何打包js文件,但是如果我们想要打包css、vue、ts等文件时,需要先给webpack扩展对应的loader,这里以css为例演示
进入https://www.webpackjs.com/loaders/,找到对应的css的loader,按照文档操作
npm install --save-dev css-loader
npm install style-loader --save-dev
module.exports
对象中添加module
对象 module: {
rules: [
{
test: /\.css$/,
// css-loader:只负责加载,但不负责解析,也不负责与html文件连接
// style-loader:将模块的导出作为样式添加到 DOM 中
// webpack在使用多个loader时,是从右向左读取,我们要先加载css,在添加样式,所以要将css-loader放到右边
use: [ 'style-loader', 'css-loader' ]
}
]
}
安装并配置完css的loader,我们就可以开始打包了
我们使用webpack打包文件时,只会打包入口js文件所依赖的文件,不被它依赖的文件不会被打包;所以我们将css等文件添加为入口js的依赖就能带着css文件一起打包了
// 依赖css文件
require('./css/normal.css')
如果我们希望在项目中使用scss、less、stylu来写样式,webpack是否可以帮助我们处理呢?我们这里以scss为例,其他也是一样的
注意:我们使用Live Sass Compiler插件会实时将scss文件转换为css文件,这些css文件可以作为我们开发时调整样式使用,在webpack打包时只打包scss文件即可
步骤:
require('./css/special.scss')
npm install sass-loader node-sass webpack --save-dev
npm run build
,如果报错getResolve is not a function,则是因为scss安装的版本过高,需卸载重装其他版本// 在webpack.config.js中配置loader
{
test: /\.scss$/,
use: [{
loader: "style-loader" // 将 JS 字符串生成为 style 节点
}, {
loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
}, {
loader: "sass-loader" // 将 Sass 编译成 CSS
}]
}
我们在images文件夹加入两张图片:一张小于8kb的图片和一张大于8kb的图片,待会儿我们会针对这两张不同大小的图片进行不同的处理
我们已经将normal.css文件作为main.js的依赖了,所以我们在该css文件中设置背景图片也会作为main.js的依赖;但我们需要为图片单独添加loader
步骤:
npm install --save-dev url-loader
{
// 设置匹配的图片格式
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// 限制图片大小,图片超出limit会用file-loader来打包,需安装file-loader
// 图片小于limit时,会将图片url地址编译成base64格式字符串
limit: 8192
}
}
]
}
当我们使用的图片超出了url-loader配置的大小范围,则不会再以url-loader来进行加载,而是以file-loader来加载
如果没有安装file-loader则会报错
npm install --save-dev file-loader
,无需对file-loader专门进行配置当我们使用file-loader加载图片后,发现在dist文件夹下生成了一个以hash值命名的图片,也就是我们的源图片;但在网页中该图片并没有显示,因为webpack打包后的文件url路径默认是根目录,而不是dist目录,因此需要修改默认url路径
publicPath
属性,以后任何涉及到url的路径,都会在前面拼接一个dist/
output: {
publicPath: 'dist/'
},
注意:此时我们的index.html在项目根目录,所以访问资源的时候需要添加dist/
路径;到后期我们也会将index.html打包到dist目录下,到时候需要将publicPath
属性删掉
使用file-loader打包文件时会在dist文件夹生成一个32位hash值命名的文件,目的是防止文件名重复
但是在真实开发中我们希望将所有的图片放在dist下的images文件夹下,附带图片原来的名称,同时也要防止重复
因此我们需要在webpack.config.js文件中进行配置:
{
// 1. 找到url-loader的配置项
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'url-loader',
options: {
limit: 10000,
// 2. 在options中添加name属性
name: 'images/[name].[hash:8].[ext]'
}
}]
}
images/[name].[hash:8].[ext]
:表示生成的文件会在dist的images文件夹下并以原来的名字+8位哈希值+原后缀命名,如:欧阳娜娜.d96873f5.jpg
webpack在打包我们的js文件的时候并没有完全的将ES6语法转换为ES5,因此我们需要借助babel来帮我们转换
步骤:
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
{
test: /\.js$/,
// 排除掉node_modules文件夹
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
npm install vue --save
import Vue form 'vue'
from vue
即可,如果不加相对路径,会默认去node_modules文件夹中导入,而我们的Vue就安装在此文件夹export default Vue
,所以我们直接import Vue
即可
,而我们的其实就是Vue实例的template,所以会报错
- 修改vue构建版本:进入webpack.config.js文件,在module.exports对象中添加一条属性:
resolve: {
alias: {
// 下次import Vue from 'vue'的时候会来这里看'vue'是否指向了具体的文件,这里指向了vue.esm.js
'vue$': 'vue/dist/vue.esm.js'
}
}
- 重新打包
编写Vue代码
假设我们实例化了一个Vue对象:
const app = new Vue({
el:'#app',
data:{
msg:'我是一条msg'
}
})
我们以前想要输出msg的时候会这样写:
<div id="app">
{
{msg}}
div>
在真实开发中我们的
里面不会写任何内容,我们会写在Vue对象的template
属性中,用反单引号包裹:
new Vue({
el:'#app',
template:`
{
{message}}
`,
data:{
message:'我是一条message'
},
methods:{
click() {
console.log('我被点击了')
}
}
})
这样在编译程序的时候,template
会找到id为app的DOM元素,并将其覆盖替换为template
中的代码
抽取template
前面我们将
代码和它所需要的message
数据和click()
方法都写到了Vue对象中,显得非常冗余,因此我们要将它们抽取成一个单独的组件
// 创建APP组件:
const APP = {
template:`
{
{message}}
`,
data() {
return {
message:'我是一条message'
}
},
methods: {
click() {
console.log('我被点击了')
}
}
}
// 现在的Vue对象:
new Vue({
el:'#app',
template:``,
data:{
},
methods:{
}
})
我们想要使用该组件,则需要在Vue对象中注册它,然后直接在template
属性中使用它即可,因为template
中的内容会覆盖替换掉id为app的DOM元素
new Vue({
el:'#app',
// 将APP组件中的内容覆盖替换id为app的DOM元素,注意组件的单标签形式 斜杠在右边
template:' ',
// 注册APP组件
components:{
APP
}
})
虽然我们将该组件抽取了出来,但是main.js作为程序入口,不应该保存这些组件代码,而且当组件过多时代码显得更加混乱,因此我们要为该组件创建一个单独的js文件单独保存
在src目录下创建一个vue文件夹,再创建一个APP.js文件,在该js文件里面专门书写该APP组件,并导出该组件供其他文件引用:
export default {
template: `
{
{message}}
`,
data() {
return {
message: '给我一个吻'
}
}
}
导出了该组件,我们就可以在main.js文件中导入使用了:
import APP from './vue/APP'
// 直接在Vue对象中使用APP组件
new Vue({
el: '#app',
// APP组件的内容会替换到id为app的DOM元素上
template: ' ',
components: {
APP
}
})
虽然我们用APP.js单独保存该APP组件,但是该组件的模板和js代码没有进行分离,所以用js文件保存组件是不合适的,要使用 .vue文件
我们要在vue文件夹下创建APP.vue文件,vue格式文件包含了三部分:
:书写组件标签
:书写js代码
:书写样式
我们将以前的APP.js文件中的代码按照分类放入vue格式的文件中:
<template>
<div>
<button>我是templatebutton>
<h2>{
{message}}h2>
div>
template>
<script>
export default {
data() {
return {
message: "给我一个吻"
};
},
};
script>
<style>
style>
是不是感觉条理清晰了很多,而且我们可以为
内的标签添加样式,写在下面的
标签中即可
删除以前导入的js文件,修改为:import APP from './vue/APP.vue'
vue-loader
我们webpack加载css、scss都需要下载对应的loader,加载vue也不例外:
- 安装:
npm install vue-loader vue-template-compiler --save-dev
- 配置:进入webpack.config.js文件,在module对象的rules数组里添加一个新的对象元素:
{
test: /\.vue$/,
use: ['vue-loader']
}
此时我们执行webpack打包会报错,因为我们的vue-loader版本过高,我们可以配置为较低的版本来解决此问题
进入package.json,将vue-loader
的值改为^13.0.0
,这个^
的意思是会将版本修改为大于13.0的版本但小于14.0版本中的一个,然后执行npm install
会自动安装该版本
- 重新打包
组件化开发Vue
下面我们来感受一下什么是真正的组件化开发
- 我们在vue文件夹下再新建一个Cpn.vue,在
标签内书写一些内容,最后的代码如下:
<template>
<div>
<h2>我是Cpn里的文字h2>
<h2>我是Cpn里的文字h2>
<h2>我是Cpn里的文字h2>
div>
template>
<script>
export default {
};
script>
<style>
style>
- 打开之前的APP.vue文件,在
标签中导入刚才创建的Cpn组件:import Cpn from './Cpn.vue'
- 注册刚导入的Cpn组件
- 在APP的
中使用Cpn组件
此时的APP.vue整体代码如下:
<template>
<div>
<button>我是templatebutton>
<h2 class="title">{
{message}}h2>
<Cpn />
<Cpn />
div>
template>
<script>
// 导入Cpn组件
import Cpn from "./Cpn.vue";
export default {
data() {
return {
message: "给我一个吻",
};
},
// 注册一下刚导入的Cpn组件
components: {
Cpn,
},
};
script>
<style scoped>
.title {
color: green;
}
style>
以后我们的开发模式都是这样写组件的,到时候我们的应用程序就是一个组件树
以现在的代码为例,main.js引入了APP组件,APP组件就是根组件,而根组件又引入了Cpn组件,Cpn组件以后又引入了其他组件,每个组件都是一个独立的文件
导入组件省略扩展名
进入webpack.config.js文件,在resolve对象中添加:
// 导入js和vue后缀的文件时不需在写扩展名,这两个必须同时添加
extensions: ['.vue', '.js']
webpack中的插件
webpack中的插件(plugin),就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等
loader和plugin区别
- loader主要用于转换某些类型的模块,它是一个转换器
- plugin是插件,它是对webpack本身的扩展,是一个扩展器
plugin的使用过程:
- 通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
- 在webpack.config.js中的plugins中配置插件
添加版权插件
我们先来使用一个最简单的插件,为打包的文件添加版权声明
该插件名字叫BannerPlugin,属于webpack自带的插件
- 进入webpack.config.js,由于该插件集成到了webpack包中,所以我们要导入webpack包
- 在module.exports对象中添加plugins数组
- 配置BannerPlugin
const webpack = require('webpack')
module.exports = {
...
plugins: [
new webpack.BannerPlugin('最终版权归我所有~')
]
}
HTML打包插件
目前,我们的index.html文件是存放在项目的根目录下的
在真实发布项目时,发布的是dist文件夹中的内容,所以我们需要将index.html文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件
该插件的作用:
- 自动生成一个index.html文件(可以指定模板来生成)
- 将打包的js文件,自动通过script标签插入到body中
使用步骤:
- 安装插件:
npm install html-webpack-plugin --save-dev
,最高版本会报错,建议安装3.0版本
- 引入插件:在webpack.config.js中输入:
const HtmlWenpackPlugin = require('html-webpack-plugin')
- 在plugins数组中添加:
new HtmlWebpackPlugin()
现在重新打包就能在dist文件夹生成我们的index.html文件了,但是还存在两个问题:
- 我们之前学习的时候给file-loader的src路径添加了
dist/
,现在我们的html文件已经被打包到该路径了,所以不再需要
- 我们希望自动生成index.html的时候添加一个
标签
解决:
- 进入webpack.config.js,将
output
对象中的publicPath: 'dist/'
属性删掉
- 将根目录下的index.html文件作为打包html文件时的模板:进入webpack.config.js文件,在plugins数组中的配置中添加:
new HtmlWebpackPlugin({
// 以当前配置文件所在目录下的index.html为模板
template: 'index.html'
})
js压缩插件
- 安装:
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
- 修改webpack.config.js文件:
// 引入插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
...
plugins:[
new UglifyJsPlugin()
]
}
搭建本地服务器
之前我们每次调试一点代码都要重新打包,很不方便
webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果
- 安装
npm install --save-dev webpack-dev-server@2.9.1
- 在webpack.config.js文件中配置devServer
devServer也是作为webpack中的一个选项,选项本身可以配置如下属性:
contentBase
:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
port
:端口号
inline
:页面实时刷新
historyApiFallback
:在SPA页面中,依赖HTML5的history模式
// 在webpack.config.js文件中配置
module.exports = {
...
devServer: {
contentBase: './dist',
inline: true
}
}
如果直接在控制台输入webpack-dev-server
来运行的话会执行全局webpack,因此我们可以在package.json的scripts对象中添加脚本:"dev": "webpack-dev-server --open"
,(添加–open,在开启服务时会自动打开浏览器)
这样我们只需要在终端输入npm run dev
,就可以在本地webpack运行我们的服务了,我们修改src目录下的文件代码时,浏览器就会自动刷新
想要终止服务,在终端按下ctrl+c
选择退出
总结:我们通过 webpack-dev-server
搭建了一个本地服务,之后我们的测试都是在本地服务中进行测试,等全部测试完毕之后,就可以执行 npm run build
进行真正的打包,再把打包后的dist文件夹放到服务器进行部署
配置文件的抽离
在webpack.config.js中,有一些配置是在开发环境中不需要的,如js压缩插件;有一些配置是生产环境不需要的,如devserver
我们希望将配置文件分为三份,分别为公共配置、开发配置、生产配置。在程序开发时,执行公共配置和开发配置;在程序上线后,执行公共配置和生产配置,因此还需要文件合并工具
步骤:
- 安装文件合并工具:
npm install webpack-merge --save-dev
,推荐安装4.0版本,高版本会报错,参考https://blog.csdn.net/sinat_37503265/article/details/107216055
- 在根目录新建build文件夹用来保存配置文件
- 新建base.config.js,dev.config.js,prod.config.js三个配置文件
- 将我们以前的webpack.config.js中的配置项按照分类分别放到这三个文件中
- 合并文件,这里以合并公共配置和生产配置为例,打开生产环境配置文件:
// 导入js压缩插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
// 导入webpack-merge插件
const webpackMerge = require('webpack-merge')
// 导入base.config.js
const baseConfig = require('./base.config')
// exports导出的时候,将base.config和prod.config一起导出
module.exports = webpackMerge(baseConfig, {
// 原来prod.config中的代码
plugins: [
new UglifyJsPlugin()
],
})
- 修改默认配置文件,修改package.json文件中的
build
和dev
脚本,添加--config
字段
{
...
"scripts": {
"build": "webpack --config ./build/prod.config.js",
"dev": "webpack-dev-server --open --config ./build/dev.config.js"
},
}
- 修改webpack打包目录,进入base.config.js,修改path:
module.exports = {
output: {
path: path.resolve(__dirname, '../dist')
},
}