Webpack实践
webpack介绍和安装
webpack介绍
为什么需要webpack
由于前端之前js、css、图片文件需要单独进行压缩和打包,这样团队人员处理很繁琐,然后 Instagram 团队就想让这些工作自动化,然后webpack应运而生。
是什么
webpack是一个模块打包器(module bundler),webpack视HTML,JS,CSS,图片等文件都是一种 资源 ,每个资源文件都是一个模块(module)文件,webpack就是根据每个模块文件之间的依赖关系将所有的模块打包(bundle)起来。
有什么用
- 对 CommonJS 、 AMD 、ES6的语法做了兼容
- 对js、css、图片等资源文件都支持打包(适合团队化开发)
- 比方你写一个js文件,另外一个人也写一个js文件,需要合并很麻烦,现在交给webpack合并很简单
- 有独立的配置文件
webpack.config.js
- 可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间
- 具有强大的Plugin(插件)接口,大多是内部插件,使用起来比较灵活
webpack使用的名词
webpack的核心概念分为
入口(Entry)
、加载器(Loader)
、插件(Plugins)
、出口(Output)
入口(Entry)
:入口起点告诉 webpack 从哪里开始,并根据依赖关系图确定需要打包的文件内容加载器(Loader)
:webpack 将所有的资源(css, js, image 等)都看做模块,但是 webpack 能处理的只是 JavaScript,因此,需要存在一个能将其他资源转换为模块,让 webpack 能将其加入依赖树中的东西,它就是 loader。loader用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。如less-loader
,css-loader
,sass-loader
,url-loader
,file-loader
插件(Plugins)
:loader 只能针对某种特定类型的文件进行处理,而 plugin 的功能则更为强大。在 plugin 中能够介入到整个 webpack 编译的生命周期,Plugins用于解决 loader 无法实现的其他事情,也就是说loader是预处理文件,那plugin 就是后处理文件。如:代码压缩从而减小文件体积,热更新(浏览器实时显示)出口(Output)
:指定打包后文件的路径目录和文件名
与grunt/gulp的对比
grunt/gulp的核心是Task
,我们可以配置一系列的task
(任务),并且定义task
要处理的事务(例如ES6、ts转化,图片压缩,scss转成css)之后让grunt/gulp来依次执行这些task,而且让整个流程自动化。所以grunt/gulp也被称为前端自动化任务管理工具。
我们来看一个gulp的task下面的task就是将src下面的所有js文件转成ES5的语法。并且最终输出到dist文件夹中。
const gulp = require('gulp');
const babel = require('gulp-babel');
gulp.task('js',()=>
gulp.src('src/*.js)
.pipe(babel({
presets:['es2015]
}))
.pipe(gulp.dest('dist'))
);
什么时候用grunt/gulp呢?
如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念。只需要进行简单的合并、压缩,就使用grunt/gulp即可。但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack了。
所以,grunt/gulp和webpack有什么不同呢?
grunt/gulp
更加强调的是前端流程的自动化,模块化不是它的核心。webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能。
webpack使用环境准备
安装Node.js
webpack
基于node.js
运行,首先需要安装node.js
为什么会有node.js?
传统意义上的 JavaScript 运行在浏览器上,Chrome 使用的 JavaScript 引擎是 V8,Node.js 是一个运行在服务端的框架,它的底层就使用了 V8 引擎,这样就可以使用javascript去编写一些服务端的程序,这样也就实现了用 javaScript去开发 Apache + PHP 以及 Java Servlet所开发的服务端功能,这样做的好处就是前端和后端都采用 javascript,即开发一份js程序即可以运行在前端也可以运行的服务端,这样比一个应用使用多种语言在开发效率上 要高,不过node.js属于新兴产品,一些公司也在尝试使用node.js完成一些业务领域,node.js基于V8引擎,基于事件驱动机制,在特定领域性能出色,比如用node.js实现消息推送、状态监控等的业务功能非常合适。
-
下载对应你系统的Node.js版本: https://nodejs.org/en/download/ 推荐下载LTS版本
-
选安装目录进行安装,默认即可,安装完成检查PATH环境变量是否设置了node.js的路径。
-
在命令提示符下输入命令
node -v
测试,会显示当前node的版本C:\Users\L15096000421>node -v v12.13.0
安装NPM
1、自动安装NPM
npm全称Node Package Manager
,他是node包管理和分发的工具,使用NPM可以对应用的依赖进行管理,NPM的功能和服务端项目构建工具maven差不多,我们通过npm 可以很方便地下载js
库,打包js
文件。 node.js
已经集成了npm
工具,在命令提示符输入 npm -v 可查看当前npm版本
C:\Users\L15096000421>npm -v
6.13.2
2、设置包路径
包路径就是npm
从远程下载的js
包所存放的路径;
使用 npm config ls
查询NPM管理包路径(NPM下载的依赖包所存放的路径)
npm@6.13.2 C:\Users\L15096000421\AppData\Roaming\npm\node_modules\npm
NPM默认的管理包路径在C:/用户/[用户名]/AppData/Roming/npm/node_meodules,为了方便对依赖包管理,我们可以将管理包的路径设置在单独的地方,本教程将安装目录设置在node.js的目录下,创建npm_modules
和 npm_cache
,执行下边的命令:
npm config set prefifix "C:\Program Files\nodejs\npm_modules"
npm config set cache "c:\Program Files\nodejs\npm_cache"
此时再使用 npm config ls 查询NPM管理包路径发现路径已更改
3、安装cnpm
npm默认会去国外的镜像去下载js包,在开发中通常我们使用国内镜像,这里使用淘宝镜像;
输入命令,进行全局安装淘宝镜像。
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装后,我们可以使用cnpm -v
命令来查看cnpm的版本
C:\Users\L15096000421>cnpm -v
cnpm@6.1.1 (C:\Users\L15096000421\AppData\Roaming\npm\node_modules\cnpm\lib\parse_argv.js)
npm@6.14.5 (C:\Users\L15096000421\AppData\Roaming\npm\node_modules\cnpm\node_modules\npm\lib\npm.js)
node@12.13.0 (D:\kaifagongju\node\node install\node.exe)
npminstall@3.27.0 (C:\Users\L15096000421\AppData\Roaming\npm\node_modules\cnpm\node_modules\npminstall\lib\index.js)
prefix=C:\Users\L15096000421\AppData\Roaming\npm
win32 x64 10.0.18362
registry=https://r.npm.taobao.org
nrm ls
查看镜像是否已经指向taobao
C:\Users\L15096000421>nrm ls
* npm -------- https://registry.npmjs.org/
yarn ------- https://registry.yarnpkg.com/
cnpm ------- http://r.cnpmjs.org/
taobao ----- https://registry.npm.taobao.org/
nj --------- https://registry.nodejitsu.com/
npmMirror -- https://skimdb.npmjs.com/registry/
edunpm ----- http://registry.enpmjs.org/
使nrm use XXX
切换镜像
如果nrm没有安装则需要进行全局安装:cnpm install -g nrm
安装webpack
webpack
安装分为本地安装和全局安装:
本地安装: 仅将webpack安装在当前项目的node_modules
目录中,仅对当前项目有效。
全局安装: 将webpack安装在本机,对所有项目有效,全局安装会锁定一个webpack
版本,该版本可能不适用某个 项目。全局安装需要添加 -g
参数
1、本地安装
只在我的项目中使用webpack,需要进行本地安装,因为项目和项目所用的webpack的版本不一样。本地安装就会 将webpack的js包下载到项目下的npm_modeuls
目录下。
npm install --save-dev webpack 或 cnpm install --save-dev webpack
npm install --save-dev webpack-cli (4.0以后的版本需要安装webpack-cli)
--save-dev
可以使用-D
参数代替
--save
:将配置信息保存到package.json中,同时--save
:也是项目生产环境,项目发布之后还依赖的东西,保存在dependencies例如:如果你用了 jQuery,由于发布之后还是依赖
jQuery
,所以是dependencies
--save-dev
:是项目开发环境依赖的东西,保存在devDependencies中例如:写 ES6 代码,如果你想编译成
ES5
发布那么babel
就是devDependencies
2、全局安装加-g
全局安装就将webpack
的js
包下载到npm
的包路径下。
npm install webpack -g 或 cnpm install webpack -g
3、安装webpack指定的版本,不指定代表安装最新的
本地安装: cnpm install --save-dev webpack@3.6.0
全局安装:npm install webpack@3.6.0 -g或 cnpm install webpack@3.6.0 -g
4、测试:在cmd状态输入webpack -v
,出现如下提示说明 webpack安装成功
E:\webpacktest>webpack -v
4.43.0
为什么全局安装后,还需要局部安装呢?
在终端直接执行webpack命令,使用的全局安装的webpack;当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack
webpack配置使用
测试环境
- WebStorm 2020.1.2 x64
- node — v12.13.0
- webpack — 4.43.0
- npm — 6.13.2
搭建项目结构
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="first">
<input type="button" id="btn" value="+">
<input type="text" id="two">
<input type="button" id="btn" value="=">
<input type="text" id="res">
<script src="./dist/js/bulid.js"></script>
</body>
</html>
js
moudle1.js
function sum(x,y){
return x + y;
}
// 导出 sum 函数
module.exports = sum;
main.js
// 1、获取index.html中的dom对象
var first = document.getElementById('first');
var btn = document.getElementById('btn');
var two = document.getElementById('two');
var res = document.getElementById('res');
btn.onclick = function(){
var firstValue = parseFloat(first.value);
var twoValue = parseFloat(two.value);
//2、获取 module1.js中的 sum函数
//http://www.ruanyifeng.com/blog/2015/05/require.html
var sum = require('./module1.js');
res.value = sum(firstValue,twoValue);
}
webpack.config.js(配置文件)
const path = require('path'); // 首先要引入node.js中path 模块,用于处理文件与目录的路径
// const 命令声明一个只读的常量,一旦声明,值不可以改变,改变会报错;只声明不赋值也会报错
// 常量存储的是一个不可以变化的变量。
module.exports = {
entry:'./src/./js/main.js', // 指定入口文件:可以是字符串/数组/对象,这里我们入口只有一个,所以写一个字符串即可
output:{ //出口:通常是一个对象,里面至少包含两个重要属性,path和filename
path: path.resolve(__dirname, './dist'), // 指定出口文件的路径目录
filename: 'bulid.js' // 制定出口文件的名称
}
}
初始化项目
进入到根目录webpacktest
,使用 cnpm init
指令创建项目描述文件 package.json
文件。
cnpm init
命令行里会以交互的形式让你填一些项目的介绍信息,依次介绍如下:(不知道怎么填的直接回车、回车…)
- name 项目名称,不填则默认更目录
- version 项目的版本号,不填则默认1.0.0
- description 项目的描述信息
- entry point 项目的入口文件,不填则默认index.js,后面可以修改
- test command 项目启动时脚本命令
- git repository 如果你有 Git 地址,可以将这个项目放到你的 Git 仓库里
- keywords 关键词
- author 作者叫啥
- license 项目要发行的时候需要的证书,平时玩玩忽略它
测试
根目录webpacktest
新建app.js
console.log('我很帅')
执行命令node app.js
证明了node是提供了JavaScript的运行环境
当前项目安装Webpack依赖包
只在我的项目中使用webpack,需要进行本地安装,因为项目和项目所用的webpack的版本不一样。本地安装就会 将webpack的js包下载到项目下的npm_modeuls
目录下。
cnpm install --save-dev webpack
cnpm install --save-dev webpack-cli (4.0以后的版本需要安装webpack-cli)
--save-dev
可以使用-D
参数代替
--save
:将配置信息保存到package.json中,同时--save
:也是项目生产环境,项目发布之后还依赖的东西,保存在dependencies例如:如果你用了 jQuery,由于发布之后还是依赖
jQuery
,所以是dependencies
--save-dev
:是项目开发环境依赖的东西,保存在devDependencies中例如:写 ES6 代码,如果你想编译成
ES5
发布那么babel
就是devDependencies
打包测试
loader的使用
在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖。
但是,在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。对于webpack本身的能力来说,对于这些转化是不支持的。那怎么办呢?给webpack扩展对应的loader就可以啦。
loader使用过程:
- 通过npm安装需要使用的loader
- 在webpack.config.js中的modules关键字下进行配置
实现CSS打包
- 本地安装所需的加载器
cnpm install css-loader style-loader --save-dev
2.在src—>css目录中新建main.css
#first{
border: 1px solid red;
}
3.在webpack.config.js中配置相关的loader
const path = require('path'); // 首先要引入node.js中path 模块,用于处理文件与目录的路径
// const 命令声明一个只读的常量,一旦声明,值不可以改变,改变会报错;只声明不赋值也会报错
// 常量存储的是一个不可以变化的变量。
//
module.exports = {
entry:'./src/./js/main.js', // 指定入口文件
output:{
path: path.resolve(__dirname, './dist/js'), // 指定出口文件的路径目录
filename: 'bulid.js' // 制定出口文件的名称
},
module:{
rules:[
// 在webpack2中,loaders 被替换成了 rules 其实就是loader的规则
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
// test 说明了当前 loader 能处理那些类型的文件
// use 则指定了 loader 的类型。
// 注意:这次因为webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的。
}
]
}
}
4.在main.js
中获取css
目录中的main.css
文件,webpack会根据入口文件所有依赖的文件进行打包
// 1、获取index.html中的dom对象
var first = document.getElementById('first');
var btn = document.getElementById('btn');
var two = document.getElementById('two');
var res = document.getElementById('res');
btn.onclick = function(){
var firstValue = parseFloat(first.value);
var twoValue = parseFloat(two.value);
//2、获取 module1.js中的 sum函数
//http://www.ruanyifeng.com/blog/2015/05/require.html
var sum = require('./module1.js');
res.value = sum(firstValue,twoValue);
}
// 3、获取css目录中的main.css文件
require('../css/main.css');
在终端中输入 webpack
命令进行css文件打包
package.json
中定义启动
每次执行都敲这么一长串有没有觉得不方便呢?我们可以在package.json
的scripts
中定义自己的执行脚本。
package.json
中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。
- 首先,会寻找本地的node_modules/.bin路径中对应的命令。
- 如果没有找到,会去全局的环境变量中寻找。
- 如何执行我们的build指令呢?
cnpm run build
实现SCSS打包
本地安装对应的loader
cnpm install sass-loader node-sass webpack --save-dev
在src目录中新建 sass目录–> scss1.scss
// scss1.scss文件
$color:purple;
#two{
border:1px solid $color;
}
在webpack.config.js
中配置相关的loader,**注意:**在 rules
数组中配置
{
test: /\.scss$/,
// 注意 是sass-loader,不是 scss-loader
//安装:cnpm install sass-loader css-loader style-loader node-sass webpack --save-dev
use: [ 'style-loader', 'css-loader', 'sass-loader' ]
}
在js目录中 main.js里面引入 scss1.scss
// 4、获取sass目录中的scss1.scss文件
require('../sass/scss1.scss');
在终端中输入 webpack
命令进行scss文件打包
实现Less打包
本地安装对应的loader加载器
cnpm install less less-loader css-loader style-loader webpack --save-dev
在在src目录中新建less目录–> less1.less
@color:blue;
#res{
border:1px dashed blue;
}
在webpack.config.js
中配置相关的loader,**注意:**在 rules
数组中配置
// 实现 less 打包
{
test: /\.less$/,
use: [ 'style-loader', 'css-loader', 'less-loader' ]
}
在js目录中 main.js里面引入 less1.less文件
// 5、获取less目录中的less1.less文件
require('../less/less1.less');
在终端中输入 webpack
命令进行scss文件打包
实现打包url资源(图片、gif、图标等)功能
-
在src 目录中 新建imgs目录,放入两张不同大小的图片,一张较小的图片vue1.png’(小于8kb),一张较大的图片vue_cover_logo.png(大于8kb)
-
在index.html中新增
<div id="bg1"></div>
<div id="bg2"></div>
-
在css文件夹新增–>icons-extra.css
#bg1{ width: 1500px; height: 500px; background: url('../imgs/vue1.png') no-repeat; } #bg2{ width: 500px; height: 500px; background: url('../imgs/vue_cover_logo.png') no-repeat; }
如果我们现在直接打包,会出现如下问题
- 图片处理,我们使用url-loader来处理,依然先本地安装url-loader加载器
cnpm install url-loader --save-dev
-
在webpack.config.js中配置相关的loader
// 实现 url 资源打包 { // 图片和字体文件使用 url-loader 来处理 test: /\.(png|jpg|gif|ttf|eot|woff|woff2|svg)$/, use: [ { loader: 'url-loader', // options 为可以配置的选项 options: { limit: 8192 // limit=8192表示将所有小于8kb的图片都转为base64形式(为什么 呢?因为一个很小的图片,不值当的去发送一个请求,减少请求次 数。) // (其实应该说超过8kb的才使用 file-loader 来映射到文件,否 则转为dataurl形式) } } ] //保证输出的图片名称与之前命名的图片名称保持一致(目前只是支持这样的写法, webpack3 没有响应的options进行配置) use:'url-loader?limit=8192&name=[path][name].[ext]' }
在main.js中引入css目录中icons-extra.css的文件
// 6、获取src目录中的mui目录中icons-extra.css的文件 require('../mui/css/icons-extra.css');
在终端中输入
npm run build
命令进行icons-extra.css文件打包,而仔细观察,你会发现背景图是通过base64
显示出来的;OK,这也是limit属性的作用,当图片小于8kb时,对图片进行base64编码
那么问题来了,如果大于8kb呢?我们将background的图片改成vue_cover_logo.png
这次因为大于8kb的图片,会通过file-loader
进行处理,但是我们的项目中并没有file-loader
所以,我们需要安装file-loader
cnpm install file-loader --save-dev
再次打包,就会发现dist文件夹下多了一个图片文件
我们发现webpack
自动帮助我们生成一个非常长的名字;
这是一个32位hash值,目的是防止名字重复。但是,真实开发中,我们可能对打包的图片名字有一定的要求。
比如,将所有的图片放在一个文件夹中,跟上图片原来的名称,同时也要防止重复 ,所以,我们可以在options中添加上如下选项:
- img:文件要打包到的文件夹
- name:获取图片原来的名字,放在该位置
- hash:8:为了防止图片名称冲突,依然使用hash,但是我们只保留8位
- ext:使用图片原来的扩展名
但是,我们发现图片并没有显示出来,这是因为图片使用的路径不正确
默认情况下,
webpack
会将生成的路径直接返回给使用者。 但是,我们整个程序是打包在dist文件夹下的,所以这里我们需要在路径下再添加一个dist/
百度查配置文件
module.exports = {
// 入口文件路径,__dirname是根目录
entry: __dirname + '/src/main.js',
// 打包生成文件
output: {
path: __dirname + '/output',
filename: 'main.js'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.jpeg$/,
use: 'url-loader?limit=1024&name=[path][name].[ext]&outputPath=img/&publicPath=output/',
}
]
}
}
}
这里涉及到了4个参数:limit、name、outputPath、publicPath。
-
limit
参数就是告诉url-loader,在文件小于多少个字节时,将文件编码并返回DataURL。 -
name
表示输出的文件名规则,如果不添加这个参数,输出的就是默认值:文件哈希。加上[path]表示输出文件的相对路径与当前文件相对路径相同,加上[name].[ext]则表示输出文件的名字和扩展名与当前相同。加上[path]这个参数后,打包后文件中引用文件的路径也会加上这个相对路径。不加[path],可能引图片路径会找不到404
加[path]打包后文件中引用文件的路径也会加上这个相对路径。
-
outputPath
表示输出文件路径前缀。图片经过url-loader打包都会打包到指定的输出文件夹下。但是我们可以指定图片在输出文件夹下的路径。比如outputPath=img/,图片被打包时,就会在输出文件夹下新建(如果没有)一个名为img的文件夹,把图片放到里面。 -
publicPath
表示打包文件中引用文件的路径前缀,如果你的图片存放在CDN上,那么你上线时可以加上这个参数,值为CDN地址,这样就可以让项目上线后的资源引用路径指向CDN了。
ES6转换为ES5语法
安装loader加载器
cnpm install --save-dev babel-loader babel-core babel-preset-env
babel-core
如果某些代码需要调用Babel的API进行转码,就要使用babel-core
模块babel-preset-env
通过根据您的目标浏览器或运行时环境自动确定您需要的Babel插件
配置
{
test: /\.js$/,
exclude: /(node_modules)/, // exclude 排除的意思,把 node_modules文件夹排除编译之外
use: {
loader: 'babel-loader',
options: {
// presets 预设列表(一组插件)加载和使用
presets: ['env'],
plugins: ['transform-runtime'] // 加载和使用的插件列表
}
}
}
把一些代码改成ES6 语法的写法
//moudule1.js
function sum(x,y){
return x + y;
}
// 导出 sum 函数
// module.exports = sum;
// 改成ES6 的写法语法
export default{
sum
}
main.js
var first = document.getElementById('first');
var btn1 = document.getElementById('btn1');
var two = document.getElementById('two');
var res = document.getElementById('res');
console.log(1);
btn1.onclick = function() {
var firstValue = parseFloat(first.value);
var twoValue = parseFloat(two.value);
//2、获取 module1.js中的 sum函数
//http://www.ruanyifeng.com/blog/2015/05/require.html
console.log(2);
/* var sum = require('./module1.js');
res.value = sum(firstValue,twoValue);*/
res.value = sumObj.sum(firstValue, twoValue);
}
// 3、获取css目录中的main.css文件
// require('../css/main.css');
// 把步骤3 改为 ES6写法,引入css目录中的main.css文件
import '../css/main.css';
// 4、获取sass目录中的scss1.scss文件
require('../sass/scss1.scss');
// 5、获取less目录中的less1.less文件
require('../less/less1.less');
// 6、获取src目录中的mui目录中icons-extra.css的文件
require('../mui/css/icons-extra.css');
// 把 var sum = require('./module1.js'); 写成 ES6语法
import sumObj from './module1.js'
plugin的使用
webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。
loader和plugin区别:
- loader主要用于转换某些类型的模块,它是一个转换器。
- plugin是插件,它是对webpack本身的扩展,是一个扩展器。
plugin的使用过程:
- 通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
- 在webpack.config.js中的plugins中配置插件
添加版权的Plugin
该插件名字叫BannerPlugin,属于webpack自带的插件。
plugins:[
new webpakc.BannerPlugin('最终版权归zysheep所有')
]
打包html的plugin
目前,我们的index.html
文件是存放在项目的根目录下的。我们知道,在真实发布项目时,发布的是dist
文件夹中的内容,但是dist
文件夹中如果没有index.html
文件,那么打包的js等文件也就没有意义了。所以,我们需要将index.html
文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin
插件
HtmlWebpackPlugin插件可以为我们做这些事情:
- 自动生成一个index.html文件(可以指定模板来生成)
- 将打包的js文件,自动通过script标签插入到body中
安装HtmlWebpackPlugin插件
cnpm install html-webpack-plugin --save-dev
使用插件,修改webpack.config.js文件中plugins部分的内容如下:
//引入
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
})
]
这里的template表示根据什么模板来生成index.html,另外,我们需要删除之前在
output
中添加的publicPath
属性,否则插入的script标签中的src可能会有问题
js压缩的Plugin
在项目发布之前,我们必然需要对js等文件进行压缩处理。这里,我们就对打包的js文件进行压缩我们使用一个第三方的插件uglifyjs-webpack-plugin,并且版本号指定1.1.1,和CLI2保持一致
cnpm install uglifyjs-webpack-plugin@1.1.1 --save-dev
修改webpack.config.js文件,使用插件:
new
查看打包后的bunlde.js文件,是已经被压缩过了。
Webpack-dev-server结合后端服务器的热替换配置
webpack-dev-server
提供了一个简单的 web 服务器,并且能够实现热加载 并且自动刷新浏览器,html-webpack-plugin
插件根据html模板在内存生成html文件,它的工作原理是根据模板文件在内存中生成一个index.html文件。这样的话,即使我们的目录中没有了相关js等文件,还能够加载出来,这样能够提高我们页面运行速度。
安装
webpack-dev-server
插件和html-webpack-plugin
插件
cnpm install webpack-dev-server --save-dev
cnpm install --save-dev html-webpack-plugin
在webpack.config.js中配置webpack-dev-server
注意:与module同级
devServer: {
// 为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
contentBase: path.join(__dirname, "dist"),
compress: true,
// 当它被设置为true的时候对所有的服务器资源采用gzip压缩
// 对JS,CSS资源的压缩率很高,可以极大得提高文件传输的速率,从而提升web性能
port: 9000, // 如果想要改端口,可以通过 port更改
hot: true, // 启用 webpack 的模块热替换特性()
inline: true, // 实现实时重载(实现自动刷新功能)默认情况下是 true。
host: "localhost" // 如果你希望服务器外部可访问,指定使用一个 host。默认是 localhost(也就是你可以不写这个host这个配置属性)。
}
在package.json中配置script
"start": "webpack-dev-server --open"
//"start": "webpack-dev-server --open --port 8080 --hot --inline"
// 如果在这里配置了,就不用在webpack.config.js中配置devServer属性了。
--inline:自动刷新
--hot:热加载
--port:指定端口
--open:自动在默认浏览器打开
--host:可以指定服务器的 ip,不指定则为127.0.0.1,如果对外发布则填写公网ip地址
在webpack.config.js中配置html-webpack-plugin
需要引入html-webpack-plugin 插件与webpack,并且与module同级
// 引入html-webpack-plugin 插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
plugins: [
new HtmlWebpackPlugin({
title: '首页', // 用于生成的HTML文档的标题
filename: 'index.html', //写入HTML的文件。默认为index.html。也可以指定一个子目录(例如:)assets/admin.html
template: 'index.html' // Webpack需要模板的路径
}),
new webpack.HotModuleReplacementPlugin() // 需要结合 启用热替换模块(Hot Module Replacement),也被称为 HMR
]
此时package.json
的文件内容如下:
{
"name": "webpacktest",
"version": "1.0.0",
"description": "node测试",
"main": "webpack.config.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --open"
},
"author": "zysheep",
"license": "ISC",
"devDependencies": {
"css-loader": "^3.5.3",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.3.0",
"less": "^3.11.3",
"less-loader": "^6.1.1",
"node-sass": "^4.14.1",
"sass-loader": "^8.0.2",
"style-loader": "^1.2.1",
"url-loader": "^4.1.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0"
}
}
此时webpack.config.js
的文件内容如下:
const path = require('path'); // 首先要引入node.js中path 模块,用于处理文件与目录的路径
// const 命令声明一个只读的常量,一旦声明,值不可以改变,改变会报错;只声明不赋值也会报错
// 常量存储的是一个不可以变化的变量。
// 引入html-webpack-plugin 插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './src/./js/main.js', // 指定入口文件
output: {
path: path.resolve(__dirname, './dist'), // 指定出口文件的路径目录
filename: 'bulid.js' // 制定出口文件的名称
},
module:{
rules:[
// 在webpack2中,loaders 被替换成了 rules 其实就是loader的规则
//安装 cnpm install css-loader style-loader --save-dev
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
// test 说明了当前 loader 能处理那些类型的文件
// use 则指定了 loader 的类型。
// 注意:数组中的loader不能省略扩展名
},
{
test: /\.scss$/,
// 注意 是sass-loader,不是 scss-loader
//安装:cnpm install sass-loader css-loader style-loader node-sass webpack --save-dev
use: [ 'style-loader', 'css-loader', 'sass-loader' ]
},
// 实现 less 打包 安装:cnpm install less less-loader css-loader style-loader webpack --save-dev
{
test: /\.less$/,
use: [ 'style-loader', 'css-loader', 'less-loader' ]
},
// 实现 url 资源打包
{
// 图片和字体文件使用 url-loader来处理
//安装:cnpm install url-loader file-loader --save-dev
test: /\.(png|jpg|gif|ttf|eot|woff|woff2|svg)$/,
use: [
{
loader: 'url-loader',
// options 为可以配置的选项
options: {
limit: 8192
// limit=8192表示将所有小于8kb的图片都转为base64形式(为什么呢?因为一个很小的图片,不值当的去发送一个请求,减少请求次 数。)
//(其实应该说超过8kb的才使用 url-loader 来映射到文件,否则转为dataurl形式)
}
}
],
//保证输出的图片名称与之前命名的图片名称保持一致(目前只是支持这样的写法,webpack3 没有响应的options进行配置)另外一种写法
use:'url-loader?limit=8192&name=[path][name].[ext]'
}
]
},
devServer: {
// contentBase: './dist',
// 在 localhost:8080(默认) 下建立服务,将 dist 目录下的文件,作为可访问文件 contentBase:告诉服务器从哪里提供内容
contentBase: path.join(__dirname, "dist"),
compress: true,
// 当它被设置为true的时候对所有的服务器资源采用gzip压缩
// 对JS,CSS资源的压缩率很高,可以极大得提高文件传输的速率,从而提升web性能
port: 9000, // 如果想要改端口,可以通过 port更改
hot: true, // 启用 webpack 的模块热替换特性()
inline: true, // 实现实时重载(实现自动刷新功能)默认情况下是 true。
host: "localhost" // 如果你希望服务器外部可访问,指定使用一个 host。默认是 localhost(也就是你可以不写这个host这个配置属性)。
},
plugins: [
new HtmlWebpackPlugin({
title: '首页', // 用于生成的HTML文档的标题
filename: 'index.html', //写入HTML的文件。默认为index.html。也可以指定一个子目录(例如:)assets/admin.html
template: 'index.html' // Webpack需要模板的路径
}),
new webpack.HotModuleReplacementPlugin() // 需要结合 启用热替换模块(Hot Module Replacement),也被称为 HMR
]
}
使用npm start
命令就可以实现浏览器自动更新。
问题来了,HtmlWebpackPlugin中的 title并没有显示出来,原因是需要在定义的template模板中使用ejs语法,
<title><%= htmlWebpackPlugin.options.title%></title>
再次使用npm start
命令就可以啦。
防止文件缓存(生成带有20位的hash值的唯一文件)
// webpack.config.js
output: {
path: path.resolve(__dirname, 'dist/js'), // 指定出口文件的路径目录
// filename: 'bulid.js' // 制定出口文件的名称
filename: '[name].[hash].js' // 将入口文件重命名为带有20位的hash值的唯一文件
}
抽取CSS为单独文件
安装插件从 build.js文件中提取文本(CSS)到单独的文件
cnpm install --save-dev extract-text-webpack-plugin
打包出错 Tapable.plugin is deprecated. Use new API on
.hooks
instead
原因 extract-text-webpack-plugin目前版本不支持webpack4。
解决方案 使用extract-text-webpack-plugin的最新的beta版
cnpm install -D extract-text-webpack-plugin@next
在webpack.config.js中配置
const ExtractTextPlugin = require("extract-text-webpack-plugin");
// 从 bundle 中提取文本(CSS)到单独的文件
new ExtractTextPlugin({
// 提取css,并重名为带有 20位的hash值的唯一文件
filename: '[name].[hash].css',
// 将所有的独立文件模块(这里指的是css文件)合并成一个文件
allChunks: true
})
开发环境和生产环境的分离
开发环境与生产环境分离的原因如下:
- 在开发环境中,我们使用热更新插件帮助我们实现浏览器的自动更新功能,我们的代码没有进行压缩,如果压缩了不方便我们调试代码等等,所以以上这些代码不应出现在生产环境中。
- 生产环境中,我们把项目部署到服务器时,我们会对代码进行各种各样的优化,比如压缩代码等等,这时候我们不应该把这些代码放到开发环境中,不利于代码开发和调试。
总结:针对以上这些说明,我们很有必要把区分开发环境与生产环境分离。
开发环境的配置和生产换环境配置的区别。
- 开发环境有的配置,生产环境不一定有,比如说热更新时使用到的
HotModuleReplacementPlugin
。 - 生产环境有的配置,开发环境不一定有,比如说用来压缩js用的
UglifyJsPlugin
。
如何去做?
-
因为webpack 默认找的是 webpack.config.js配置文件,所以要把开发环境的webpack.config.js配置文件改为webpack.dev.config.js代表开发环境的配置文件。
-
新建一个webpack.prod.config.js,再把开发环境中的webpack.config.js复制进去(没用的配置文件该删除的删除)
-
修改package.json文件(在
scripts
标签中添加"dev"
和"prod"
属性配置)"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack --config webpack.dev.config.js", "prod": "webpack --config webpack.prod.config.js" },
-
怎样执行命令
- 执行开发环境的中配置
cnpm run dev
- 执行生产环境的中配置
cnpm run prod
- 执行开发环境的中配置
在生产环境中配置代码压缩功能
安装插件
cnpm install --save-dev terser-webpack-plugin
配置webpack.prod.config.js
文件
devtool: "source-map",
// js代码 压缩
optimization: {
minimizer: [
new TerserPlugin({
cache: true, // 开启缓存
parallel: true, // 支持多进程
sourceMap: true,
}),
]
},
执行cnpm run prod
命令
webpack集成Vuejs开发
引入vue.js
项目中,我们会使用Vuejs
进行开发,而且会以特殊的文件来组织vue
的组件。所以,下面我们来学习一下如何在我们的webpack环境中集成Vuejs
现在,我们希望在项目中使用Vuejs,那么必然需要对其有依赖,所以需要先进行安装注:因为我们后续是在实际项目中也会使用vue的,所以并不是开发时依赖
npm install vue --save
那么,接下来就可以按照我们之前学习的方式来使用Vue
了
main.js
import Vue from 'vue'
new Vue({
el:"#app",
data:{
name:"三月三"
}
})
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack集成Vuejs</title>
</head>
<body>
<div id="app">
{{name}}
</div>
<script src="./dist/bulid.js"></script>
</body>
</html>
打包项目 – 错误信息
修改完成后,重新打包,运行程序:
打包过程没有任何错误(因为只是多打包了一个vue的js文件而已);但是运行程序,没有出现想要的效果,或浏览器中有报错
这个错误说的是我们使用的是runtime-only
版本的Vue,所以我们修改webpack的配置,添加如下内容即可
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
el和template区别(一)
正常运行之后,我们来考虑另外一个问题:
**思考:**如果我们希望将data
中的数据显示在界面中,就必须是修改index.html
如果我们后面自定义了组件,也必须修改index.html
来使用组件但是html模板在之后的开发中,我并不希望手动的来频繁修改,是否可以做到呢?
定义template
属性:在前面的Vue实例中,我们定义了el属性,用于和index.html
中的#app
进行绑定,让Vue实例之后可以管理它其中的内容这里,我们可以将div
元素中的{{message}}
内容删掉,只保留一个基本的id为div的元素,但是如果我依然希望在其中显示{{message}}的内容,应该怎么处理呢?
我们可以再定义一个template属性,代码如下:
new Vue({
el:"#app",
template:`<div id="app">{{name}}</div>`,
data:{
name:"三月三"
}
})
重新打包,运行程序,显示一样的结果和HTML代码结构。
那么,el
和template
模板的关系是什么呢?
el
用于指定Vue要管理的DOM
,可以帮助解析其中的指令、事件监听等等。- 而如果
Vue
实例中同时指定了template
,那么template
模板的内容会替换掉挂载的对应el
的模板。
这样做有什么好处呢?
这样做之后我们就不需要在以后的开发中再次操作index.html,只需要在template中写入对应的标签即可
但是,书写template
模块非常麻烦怎么办呢?
没有关系,稍后我们会将
template
模板中的内容进行抽离。会分成三部分书写:template、script、style,结构变得非常清晰。
Vue组件化开发引入
Vue
开发过程中,我们都会采用组件化开发的思想。那么,在当前项目中,如果我也想采用组件化的形式进行开发,应该怎么做呢?
查看下面的代码:当然,我们也可以将下面的代码抽取到一个js文件中,并且导出。
main.js
import Vue from 'vue'
const Cpn = {
template:`<h2>{{name}}</h2>`,
data(){
return{
name: "我是Cpn组件"
}
}
}
new Vue({
el:"#app",
template:`
<div>我的名字:{{name}}
<Cpn></Cpn>
</div>`,
data:{
name:"三月三"
},
components:{
Cpn
}
})
但是一个组件以一个js对象的形式进行组织和使用的时候是非常不方便的
- 一方面编写
template
模块非常的麻烦 - 另外一方面如果有样式的话,我们写在哪里比较合适呢?
现在,我们以一种全新的方式来组织一个vue
的组件。但是,这个时候这个文件可以被正确的加载吗?
必然不可以,这种特殊的文件以及特殊的格式,必须有人帮助我们处理。
谁来处理呢?
vue-loader以及vue-template-compiler。
安装vue-loader
和vue-template-compiler
cnpm install vue-loader vue-template-compiler --save-dev
创建.vue
文件
<template>
<h2 class="title">{{name}} </h2>
</template>
<script>
export default {
name: "Cpn",
data(){
return{
name: "我们.vue文件"
};
}
}
</script>
<style scoped>
.title{
color: pink;
}
</style>
修改webpack.config.js的配置文件:
{
//处理.vue文件
test: /\.vue$/,
use:['vue-loader']
},
注意: 打包报错?why
“vue-loader”: “^15.9.2”
指定版本:比如"vue-loader": “15.9.2”,表示安装15.9.2的版本
波浪号~+指定版本:比如 “vue-loader”: “~15.9.2”,表示安装15.9.x的最新版本(不低于15.9.0),但是不安装15.10.x,也就是说安装时不改变大版本号和次要版本号
^+指定版本:比如 “antd”: “^3.1.4”,,表示安装3.1.4及以上的版本,但是不安装4.0.0,也就是说安装时不改变大版本号。
说vue-loader在被使用的时候缺少一个插件( corresponding plugin),vue-loader从14版本开始需要配置插件,如果不想配置插件,可以使用低于14的版本
解决:引入插件
const VueLoaderPlugin = require('vue-loader/lib/plugin')
plugins: [
// 请确保引入这个插件来施展魔法
new VueLoaderPlugin()
]