webpack
一、认识webpack
之前的开发程序不能直接放到服务器,通过工具打包转化,生成让浏览器更容易识别的代码。 打包工具 grunt/gulp/webpack是/rollup(Rollup 是一个 JavaScript 模块打包器[rollup.js 中文文档] )
webpack是前端模块化打包工具,模块概念,打包概念
webpack和node和npm的关系: webpack正常运行必须依赖于node环境,node环境为了可以正常的执行很多代码,必须包含各种依赖的包,有一个npm工具,用于管理node下的各种包
使用webpack之前必须安装node,
vue cli2 脚手架2里才能看到webpack的所有配置,用3的话配置被隐藏起来了。
1. 什么是webpack
At its core, webpack is a static module bundler for modern JavaScript applications.
- 从本质上来讲,webpack是一个现代的JavaScript应用的静态模块打包工具。
- 我们从两个点来解释上面这句话:模块 和 打包
2. 前端模块化和打包
前端模块化:
- 目前使用前端模块化的一些方案:AMD、CMD、CommonJS、ES6。
- 在ES6之前,我们要想进行模块化开发,就必须借助于其他的工具,让我们可以进行模块化开发。
- 并且在通过模块化开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包。
- 而webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系。
- 而且不仅仅是JavaScript文件,我们的CSS、图片、json文件等等在webpack中都可以被当做模块来使用。
- 这就是webpack中模块化的概念。 打包如何理解:
- 理解了webpack可以帮助我们进行模块化,并且处理模块间的各种复杂关系后,打包的概念就非常好理解了。
- 就是将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。
- 并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。
- 但是打包的操作似乎grunt/gulp也可以帮助我们完成,它们有什么不同呢?
模块化开发
最初JavaScript只做一些简单的工作,随着代码量的增多,通常将代码组织在多个js文件中,会发现难以维护,如:全局变量同名问题,js文件引入顺序执行顺序的问题。 为了避免各种问题,便于维护,出现了模块化。
- 前端模块化的一些方案:AMD、CMD、CommonJS、ES6。 前三个需要底层的支撑,比如在webpack中就可以使用,webpack就可以作为底层的支撑(支持各种模块化如以上三个),因为webpack会对其代码转成浏览器可以支持识别的代码;而ES6是浏览器可以直接解析的。
开发用模块化开发,只需要用webpack打包一下,就会自动处理模块之间的依赖,就可以转成浏览器识别的文件
1) 匿名函数+模块出口
首先通过匿名函数解决重名问题;
其次当使用了匿名函数后,该js文件里的变量则为局部变量,其他文件无法访问。此时需要使用模块作为出口。
- 在匿名函数内部,定义一个对象。
- 给对象添加各种需要暴露到外面的属性和方法(不需要暴露的直接定义即可)。
- 最后将这个对象返回,并且在外面使用了一个MoudleA接受。
2) CommonJS
此语法在一些环境下才可使用,如:webpack
3)ES6的Modules(export/import)
export导出变量
export导出函数或类
export default 导入者自定义名称
import使用
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 类型需要设置为module,只有type="module"才支持这种导入导出语法 -->
<script src="aaa.js" type="module"></script>
<script src="bbb.js" type="module"></script>
<script src="mmm.js" type="module"></script>
</body>
</html>
aaa.js
var flag = true
function sum(num1, num2) {
return num1 + num2
}
if (flag) {
console.log(sum(20, 30));
}
// 1.导出方式一:
export { flag, sum }
// 2.导出方式二:
export var num1 = 1000;
export var height = 1.88
// 3.导出函数/类
export function mul(num1, num2) {
return num1 * num2
}
export class Person {
run() {
console.log('在奔跑');
}
}
// 5.export default 导入者自己命名
// export default在同一个模块中,不允许同时存在多个
export default function (argument) {
console.log(argument);
}
bbb.js
// 1.导入的{}中定义的变量
import {flag, sum} from "./aaa.js";
if (flag) {
console.log('小明是天才, 哈哈哈');
console.log(sum(20, 30));
}
// 2.直接导入export定义的变量
import {num1, height} from "./aaa.js";
console.log(num1);
console.log(height);
// 3.导入 export的function/class
import {mul, Person} from "./aaa.js";
console.log(mul(30, 50));
const p = new Person();
p.run()
// 4.导入 export default中的内容
import addr from "./aaa.js";
addr('你好啊');
// 5.统一全部导入 通过*可以导入模块中所有的export变量;给*起一个别名,方便后续的使用
// import {flag, num, num1, height, Person, mul, sum} from "./aaa.js";
import * as aaa from './aaa.js'
console.log(aaa.flag);
console.log(aaa.height);
4)AMD、CMD
3. webpack和grunt/gulp的对比
- grunt/gulp的核心是Task
- 我们可以配置一系列的task,并且定义task要处理的事务(例如ES6、ts转化,图片压缩,scss转成css)
- 之后让grunt/gulp来依次执行这些task,而且让整个流程自动化。
- 所以grunt/gulp也被称为前端自动化任务管理工具。
- 我们来看一个gulp的task
- 下面的task就是将src下面的所有js文件转成ES5的语法。
- 并且最终输出到dist文件夹中。
- 什么时候用grunt/gulp呢?
- 如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念。
- 只需要进行简单的合并、压缩,就使用grunt/gulp即可。
- 但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack了。
- 所以,grunt/gulp和webpack有什么不同呢?
- grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心。
- webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能。
二、webpack的安装
三、webpack的起步
1. 准备工作,创建文件
创建如下文件和文件夹:
文件和文件夹解析:
- dist文件夹:用于存放之后打包的文件
- src文件夹:用于存放我们写的源文件
- main.js:项目的入口文件。具体内容查看下面详情。
- mathUtils.js:定义了一些数学工具函数,可以在其他地方引用,并且使用。具体内容查看下面的详情。
- index.html:浏览器打开展示的首页html
- package.json:通过npm init生成的,npm包管理的文件(暂时没有用上,后面才会用上)
2. js文件打包(webpack src/main.js dist/bundle.js)
- 现在的js文件中使用了模块化的方式进行开发,他们不可以直接使用。 因为如果直接在index.html引入这两个js文件,浏览器并不识别其中的模块化代码。
- 另外,在真实项目中当有许多这样的js文件时,我们一个个引用非常麻烦,并且后期非常不方便对它们进行管理。
我们应该怎么做呢?使用webpack工具,对多个js文件进行打包。
我们知道,webpack就是一个模块化的打包工具,所以它支持我们代码中写模块化,可以对模块化的代码进行处理。 - 另外,如果在处理完所有模块之间的关系后,将多个js打包到一个js文件中,引入时就变得非常方便了。
- 打包后会在dist文件下,生成一个bundle.js文件。将这个js文件在index.html中引入即可
如何打包呢?使用webpack的指令即可
webpack src/main.js dist/bundle.js
bundle.js文件,是webpack处理了项目直接文件依赖后生成的一个js文件,我们只需要将这个js文件在index.html中引入即可
3. 配置入口和出口(webpack)
由于每次使用webpack的命令webpack ./src/main.js ./dist/bundle.js都需要写上入口和出口作为参数,就非常麻烦,有没有一种方法可以将这两个参数写到配置中,在运行时,直接读取呢?
- 就是创建一个webpack.config.js文件,在文件中配置入口和出口
- 终端输入npm init 初始化,一直回车生成一个package.json文件
- 在终端输入 npm install,在当前文件中安装package.json中所有的依赖
- 在终端输入 webpack 进行打包,当即在dist文件夹下生成一个bundle.js文件
新建一个webpack.config.js文件,配置webpack的出口和入口
出口是个对象类型:路径+文件名
入口是生成的js文件1、需要动态获取path路径,需要用到node语法。导入path模块 会去node对应的包里找;这时候需要pah这个包,那么就需要装个包,终端输入:npm init,一直回车生成package.json文件
当package.json里面需要依赖另外的东西的话,需要npm install,根据package.json里面所有的依赖,帮助我们在当前文件夹下安装一些东西。
2、出口的path模块中有个resolve函数,把两个路径拼接到一起。path: path.resolve(__dirname, 'dist'),
__dirname是一个全局变量,是node上下文里自带的一个全局变量,保存的就是当前webpack.config.js文件所在的路径,把当前路径拼接一个dist,整个路径就有了,此时这个路径就是个绝对路径。
3、将入口的东西和出口的东西放到一个配置里。此时只需要在终端输入:webpack,即可进行打包。 不需要再输入:webpack ./src/main.js ./dist/bundle.js
四、webpack的配置
4. 局部安装webpack(node_modules/.bin/webpack)
-
目前,我们使用的webpack是全局的webpack,如果我们想使用局部来打包呢?
因为一个项目往往依赖特定的webpack版本,全局的版本可能跟这个项目的webpack版本不一致,导出打包出现问题。 所以通常一个项目,都有自己局部的webpack。 -
第一步,项目中需要安装自己局部的webpack
这里我们让局部安装webpack3.6.0 Vue CLI3中已经升级到webpack4,但是它将配置文件隐藏了起来,所以查看起来不是很方便。
npm install webpack@3.6.0 --save-dev -
第二步,通过node_modules/.bin/webpack启动webpack打包
node_modules/.bin/webpack
5. package.json中定义启动(npm run build)
- 可以在package.json的scripts中定义自己的执行脚本
- package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。
首先,会寻找本地的node_modules/.bin路径中对应的命令。
如果没有找到,会去全局的环境变量中寻找。
如何执行我们的build指令呢?npm run build
4、如果通过npm管理的话,通常使用npm run build进行打包, 那么如何将npm run build和webpack 对应起来呢;将webpack映射到npm run build的命令。
package.json中scripts脚本,当在终端执行npm run test的时候,会在到scripts中找test,执行test对应的内容
npm run build 的时候就会去找scripts中的build
执行webpack只要在终端执行的命令,用的都是全局安装的webpack, 当在本地单独安装了webpack后,并在package.json的scripts中定义个脚本,此时在终端执行的命令用的就是本地的webpack本地的webpack是4.11.5的版本,这个项目使用的3.6.0的版本, 此时在终端执行命令时,用本地4.0的去打包本项目3.0的配置,为了避免打包出错, 需要在本地单独安装一个3.0的webpack:npm install webpack@3.6.0 --save-dev
devDependencies开发时依赖,webpack就是一个在打包完没有用的东西,所以在安装的后面加上 --save-dev
dependencies运行时依赖,在打包完还要用的东西放在这里
学完以上,在之后项目开发js时,就可随意使用模块化进行开发。
五、webpack核心loader的使用
1. 在webpack里去处理css文件
-
loader是webpack中一个非常核心的概念。
-
webpack用来做什么呢?
在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖。
但是,在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。
对于webpack本身的能力来说,对于这些转化是不支持的。
那怎么办呢?给webpack扩展对应的loader就可以啦。 -
loader使用过程:
步骤一:通过npm安装需要使用的loader(不同的loader处理不同的文件)
步骤二:在webpack.config.js中的modules关键字下进行配置 -
大部分loader我们都可以在webpack的官网中找到,并且学习对应的用法。
把css当作一个模块打包到bundle.js中,到时候只需要引用这一个文件就好
-
css文件准备
如上图,在src目录中创建一个css文件,创建normal.css文件
此时,normal.css并不会生效,没有引用。
在入口文件main.js中引入,webpack会从入口开始查找其他依赖文件
此时打包会报错,加载normal.css文件必须有对应的loader。 -
css-loader 和 style-loader 下载
webpack中文网loader使用
npm install --save-dev css-loader
npm install --save-dev style-loader -
webpack.config.js配置
const path = require('path') module.exports = { entry: './src/main.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', publicPath: 'dist/' }, module: { rules: [ { // 正则,\.对.进行转义;$结尾,匹配所有的css文件 test: /\.css$/, // css-loader只负责将css文件进行加载 // style-loader负责将样式添加到DOM中 // 使用多个loader时, 是从右向左 use: [ 'style-loader', 'css-loader' ] }, ] } } -
npm sun build 打包,运行index.html查看效果
2. 在webpack里去处理less文件
-
安装
npm install --save-dev less-loader less -
添加一个rules选项,用于处理.less文件
-
npm sun build 打包,运行index.html查看效果
3. 在webpack里去处理图片文件
-
安装
npm install --save-dev url-loader处理小图 -
安装
npm install --save-dev file-loader处理大图 -
添加一个rules选项
-
配置
publicPath: 'dist/',后续使用vue需要改掉 -
修改文件名称
options: { limit: 8192, name: 'img/[name].[hash:8].[ext]' }, -
npm sun build 打包,访问index.html查看效果
图片文件处理 – 修改文件名称
4. ES6语法处理,babel将ES6转成ES5
-
直接使用babel对应的loader,安装babel-loader
-
配置webpack.config.js文件
-
重新打包,查看bundle.js文件,发现其中的内容变成了ES5的语法
六、webpack中配置Vue
1. 引入vue.js
-
安装,引入vue,使用,打包
-
报错,由于runtime-only版本的Vue无法编译template,配置webpack
2. el和template区别
-
我们定义了el属性,用于和index.html中的#app进行绑定;用于指定Vue要管理的DOM,可以帮助解析其中的指令、事件监听等等。
-
在使用vue实例时,同时写了el和template的话,template模板的内容会替换掉挂载的对应el的模板。
-
好处是不需要再去修改index.html的代码,index.html就是固定的了。只需要在template中写入对应的标签即可。
3. vue组件化开发引入
-
如果随着代码的增多,template中的代码就越来越多,这时候就需要抽取template的代码。
将显示内容抽离成对象注册为组件使用 -
将显示内容单独放入一个文件并导出,此时模版和js代码没有分离
一个组件以一个js对象的形式进行组织和使用的时候是非常不方便的
-
一方面编写template模块非常的麻烦 -
另外一方面如果有样式的话,我们写在哪里比较合适呢?
现在,我们以一种全新的方式来组织一个vue的组件,是否可以正确加载?
-
-
将显示内容放入一个vue文件中,将模版和js进行分离。
此时再引入main.js,打包后报错,是因为无法编译vue文件,需要安装相关loader,然后在webpack中配置-
安装vue-loader和vue-template-compiler npm install vue-loader vue-template-compiler --save-dev -
修改webpack.config.js的配置文件:
一旦改掉了package.json中的内容,就需要重新npm install,重新安装依赖
-
-
将子组件导入父组件中显示
七、plugin插件的使用
认识plugin
- plugin是什么?
- plugin是插件的意思,通常是用于对某个现有的架构进行扩展。
- webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。
- loader和plugin区别
- loader主要用于转换某些类型的模块,它是一个转换器。
(用于加载某些文件对某种文件进行转化加载的时候,如css文件、vue文件) - plugin是插件,它是对webpack本身的扩展,是一个扩展器。
- loader主要用于转换某些类型的模块,它是一个转换器。
- plugin的使用过程:
- 步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
- 步骤二:在webpack.config.js中的plugins中配置插件。
下面,我们就来看看可以通过哪些插件对现有的webpack打包过程进行扩容,让我们的webpack变得更加好用。
添加版权的Plugin(BannerPlugin插件)
-
我们先来使用一个最简单的插件,为打包的文件添加版权声明
该插件名字叫BannerPlugin,属于webpack自带的插件。 -
按照下面的方式来修改webpack.config.js的文件:
-
重新打包程序:查看bundle.js文件的头部,看到如下信息
打包html的plugin(BannerPlugin插件)
- 目前,我们的index.html文件是存放在项目的根目录下的。
- 我们知道,在真实发布项目时,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js等文件也就没有意义了。
- 所以,我们需要将index.html文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件
- HtmlWebpackPlugin插件可以为我们做这些事情:
- 自动生成一个index.html文件(可以指定模板来生成)
- 将打包的js文件,自动通过script标签插入到body中
- 步骤:
-
安装HtmlWebpackPlugin插件
npm install html-webpack-plugin --save-dev -
使用插件,修改webpack.config.js文件中plugins部分的内容如下:
这里的template表示根据什么模板来生成index.html
另外,我们需要删除之前在output中添加的publicPath属性
否则插入的script标签中的src可能会有问题
-
js压缩的Plugin(uglifyjs-webpack-plugin插件)
目的是:把原来很工整的代码丑化压缩
会把所有的空格删掉,以减小文件的大小
将变量的名字用另外一些简单的符号代替
会将声明版权的注释也删掉,这个和添加版权只能使用一个
-
在项目发布之前,我们必然需要对js等文件进行压缩处理
- 这里,我们就对打包的js文件进行压缩
- 我们使用一个第三方的插件uglifyjs-webpack-plugin,并且版本号指定1.1.1,和CLI2保持一致。
- 本来webpack自带了一个对代码进行压缩的插件,CLI2使用时报错,所以使用这个插件
-
步骤
-
安装插件
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev -
修改webpack.config.js文件,使用插件。查看打包后的bunlde.js文件,是已经被压缩过了。
-
八、搭建本地服务器
webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。
步骤:
-
不过它是一个单独的模块,在webpack中使用之前需要先安装它。cli2里使用的版本。
npm install --save-dev webpack-dev-server@2.9.1
此时通过执行 webpack-dev-server 即可运行 -
配置webpack.config.js文件
- devserver也是作为webpack中的一个选项,选项本身可以设置如下属性:
- contentBase:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
- port:端口号
- inline:页面实时刷新
- historyApiFallback:在SPA页面中,依赖HTML5的history模式
- devserver也是作为webpack中的一个选项,选项本身可以设置如下属性:
-
配置package.json里的scripts:--open参数表示直接打开浏览器
九、 webpack配置分离(webpack-merge插件)
-
问题:webpack所有的配置都在webpack.config.js进行,当配置过多时,不好维护,并且需要手动修改不同环境需要依赖的配置。
-
解决:此时将webpack的配置分成三个文件,新建build文件夹及如下三个js文件
- base.config.js 生产时开发时都需要的放在这个文件里
- prod.config.js 生产环境需要的内容
- dev.config.js 开发时需要的内容
-
安装webpack-merge插件,属于开发依赖 --save-dev
npm install webpack-merge --save-dev该插件可以帮助我们将prod.config.js和dev.config.js两个配置文件进行合并。此时webpack.config.js就可以删除掉了
-
现在执行package.json里的脚本的时候会发生什么?
npm run build 报错。 没有配置文件,配置文件应该被命名为webpack.config.js -
需要在脚本里加上,指定使用哪个文件,自动找只会找webpack.config.js固定的文件
"scripts": { "build": "webpack --config ./build/prod.config.js", "dev": "webpack-dev-server --open --config ./build/dev.config.js" }, -
此时再npm run build 打包成功,但是没有打包到dist文件夹里,打包到build/dist文件夹里。
希望可以打包到项目根目录下的dist文件夹里。修改打包路径
-
内容来自视频:最全最新Vue、Vuejs教程,从入门到精通