webpack学习笔记
webpack
webpack是一个JavaScript应用的静态打包工具。
模块化
目前前端使用的一些模块化方案有:AMD、CMD、CommonJS、ES6。
在ES6之前,要想进行模块化开发,就必须借助于其他的工具,并且开发完成后还需要处理模块间的各种依赖,并且将其进行打包整合。
webpack其中一个核心就是让我们进行模块化开发,并且会帮我们处理好模块间的依赖关系。不仅仅是Js,项目的css、图片、json文件等在webpack中都可以被当做模块来使用。
ES6模块化语法
基本用法
- 在html中引入js文件时script标签type类型定义为module,即模块方式;
html:<script src="aaa.js" type="module"></script> <script src="bbb.js" type="module"></script>
- 在a.js使用 export 导出需要导出的变量、函数、类等;
let name = "Johan"
function sum(a,b) {
return a + b
}
export class Person {
constructor(name) {
this.name = name;
}
run() {
console.log(this.name + " 在奔跑");
}
}
// 导出变量name和函数sum
export {name, sum, Person}
- 在b.js使用 import 从a.js导入需要使用的变量。
// 从aaa.js导入name变量、sum函数和Person类
import {name, sum, Person} from "./aaa.js";
console.log(name);
console.log(sum(10,20));
let p = new Person("JackMa");
p.run()
其他导出导入方式:
- 定义的同时就导出:
export let age = 20;
export function mul(a, b) {
return a * b
}
import {age, mul} from "./aaa.js";
console.log(age);
console.log(mul(10,20));
- 使用default导出:
定义default导出的变量,只能导出一个:
const address = "西安市";
export default address;
使用import导入,不需要再使用{变量名}的方式导入,名称可以自定义。
import add from "./aaa.js";
console.log("address: " + add)
- 导入文件全部内容:
当需要从一个文件import的变量、函数很多时,每个都写在{变量名、方法名…}中会比较麻烦;
可以使用import * as 自定义名称 from 文件路径的方法导入文件所有内容;
import * as aaa from './aaa.js'
console.log(aaa.name);
console.log(aaa.sum(10,20));
let p = new aaa.Person("JackMa");
p.run();
打包
打包就是将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。并且打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6转成ES5语法,将TypeScript转成JavaScript等等操作。
对比gulp/grunt
- 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
首先需要安装node,版本需要不能低于8.9。
-
查看node版本:
-
全局安装webpack3.6.0:
-
局部安装webpack:
① cd 对应目录;
② 执行npm install webpack@3.6.0 --save-dev:
–save-dev`是开发时依赖,项目打包后不需要继续使用的。 -
为什么全局安装后,还需要局部安装呢?
① 在终端直接执行webpack命令,使用的全局安装的webpack。
② 当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack。
基本使用
文件和文件夹解析:
dist文件夹:(distribution 发布)用于存放之后打包的文件。
src文件夹:用于存放我们写的源文件。
main.js:项目的入口文件。具体内容查看下面详情。
aaa.js:定义了一些数学工具函数,可以在其他地方引用,并且使用。具体内容查看下面的详情。
index.html:浏览器打开展示的首页html
package.json:通过npm init -y生成的,npm包管理的文件(暂时没有用上,后面才会用上)
src目录每个js文件都采用模块化的思想开发,具体模块化规范CommonJS、AMD、ES6等都可以,最终使用webpack打包,webpack会把源码转换为浏览器可以解析的代码。
- 模块文件aaa.js:
const name = "JoHan";
// 1. CommonJS 语法
module.exports = {name};
// 2. ES6语法
export {name}
- 主文件 main.js:
// 1. CommonJS 语法
const {name} = require("./aaa.js");
// 2. ES6语法
import {name} from "./aaa";
console.log(name);
- 命令行使用webpack src/main.js dist/bundle.js,将main.js打包成bundle.js。 main.js依赖的aaa.js也会处理。
- index.html中引用打包后的js文件:
<script src="dist/bundle.js"></script>
配置webpack.config.js和package.json
配置webpack.config.js
-
每次使用webpack的命令都需要写上入口和出口作为参数会非常麻烦。
新建webpack.config.js文件:
// path 是node.js的系统库
const path = require("path");
module.exports = {
entry: "./src/main.js",
output: {
// path需要填写绝对路径
path: path.join(__dirname, "dist"),
filename: "bundle.js"
}
};
然后直接使用webpack不需要再带上路径,就可以进行打包;
局部安装webpack
-
因为一个项目往往依赖特定的webpack版本,全局的版本可能很这个项目的webpack版本不一致,导出打包出现问题。
所以通常一个项目,都有自己局部的webpack。
这里我们让局部安装webpack3.6.0。Vue CLI3中已经升级到webpack4,但是它将配置文件隐藏了起来,所以查看起来不是很方便。
注: 在cmd或者webstorm的命令直接使用webpack打包都是使用的全局webpack。
项目目录会生成node_modules目录,里面存放的node默认的包和下载的包:
-
使用局部webpack打包:
配置package.json
配置完局部webpack后,还需要指定局部webpack路径才能进行打包 node_modules.bin\webpack;
可以再package.json的script中定义自己的命令:
package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。
① 会寻找本地的node_modules/.bin路径中对应的命令。
② 如果没有找到,会去全局的环境变量中寻找。
在package.json中定义build命令,对应的值就是webpack,因为会优先去本地webpack中找命令,所以这使用的是局部webpack打包。
通过npm run build执行打包:
webpack打包css文件的配置
loader概念
在开发中不仅有基本的js代码处理,也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。
对于webpack本身的能力来说,对于这些转化是不支持的。需要给webpack扩展对应的loader。
loader使用过程:
步骤一:通过npm安装需要使用的loader
步骤二:在webpack.config.js中的modules关键字下进行配置
大部分loader我们都可以在webpack的官网中找到,并且学习对应的用法。
参考官方文档: https://www.webpackjs.com/loaders/css-loader/
基本使用
新建css目录用于存放样式文件;
js目录用于存放js文件,main.js还是放在根目录下;
-
安装loader:
style-loader: 将模块的导出作为样式添加到 DOM 中。
css-loader:解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码。
npm install --save-dev style-loader
npm install --save-dev css-loader -
在webpack.config.js中配置loader:
module.exports = {
module: {
rules: [
{
// 匹配css文件
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
}
}
- 在main.js中引用css文件,这样打包时才会找到依赖关系:
// 引用css
require("./css/main.css")
- 使用npm run build重新打包。
webpack打包css文件的配置
-
安装 less-loader和less:
less-loader:加载和转译 LESS 文件。
less:webpack会使用less对less文件进行编译。 -
在webpack.config.js中配置loader:
module.exports = {
...
module: {
rules: [{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
}]
}
};
3. 创建less文件:
4. 在main.js中引用less文件:
// 引用less
require("./css/special.less")
webpack打包图片等静态文件
-
安装 url-loader:
官方文档:https://www.webpackjs.com/loaders/url-loader/
进入项目目录执行:npm install --save-dev url-loader
-
在webpack.config.js中配置loader:
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif|jepg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
}
- 在css中设置body的图片背景:
- 重新打包: npm run build。
- 查看页面background设置的背景图片自动转换为了base64编码字符串:
图片的base64编码字符串:图片的base64编码就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址。我们所看到的网页上的每一个图片,都是需要消耗一个 http 请求下载而来的。图片的下载始终都要向服务器发出请求,要是图片的下载不用向服务器发出请求,而可以随着 HTML 的下载同时下载到本地那就太好了,而 base64 正好能解决这个问题。
-
如果图片大于limit的限制,需要安装file-loader:
6.1 将css中背景图片缓存大于30k的test02.jpg,重新打包:
6.2 会报错找不到file-loader:
6.3 执行命令安装file-loader(不需要其他配置):npm install --save-dev file-loader
6.4 安装完成后重新打包,会在dist目录下生成一张新图片:
6.5 刷新页面,报错找不到新生成的图片:
查看报错信息,是去根目录找图片,而没有去dist目录;
6.6 配置publicPath—图片加载路径:
官方文档:https://www.webpackjs.com/configuration/output/#output-publicpath
6.7 重新打包。 -
设置图片打包名称及位置:
默认的图片打包是会在dist目录下生成一张新图片,名称为32位哈希。这样把图片都放在dist根目录显得很乱,并且32位哈希太长,也看不出和原图片对应关系。
通过配置webpack.config.js可以指定生成图片的目录、名称:
如下:自动创建了img目录,并且图片名称是“原图片名称-8位hash.原扩展名”:
webpack转换ES5语法
- 查看转换后的js文件,其中还是es6的语法,有些浏览器可能不支持,所以要将其转换为es5的语法:
- 将ES6的语法转成ES5,那么就需要使用babel。而在webpack中,直接安装babel对应的loader就可以了。
执行: npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
官方文档: https://www.webpackjs.com/loaders/babel-loader/ - 配置webpack.config.js:
- 查看重新打包后的js文件没有es6的语法:
webpack中使用Vue
- 模板写在index.html中:
<body>
这是首页
<div id="app">
<h2>{{message}}</h2>
<button>按钮</button>
</div>
<script src="dist/bundle.js"></script>
</body>
- 从index.html抽出模板,放在js中:
- 从主app进一步抽取到组件中:
main.js:
// 引用vue
import Vue from 'vue'
// 定义组件---抽取模板、数据到组件中
const App = {
template: `
<div>
<h2>{{message}}</h2>
<button>按钮</button>
</div>
`,
data() {
return {
message: "Hello webpack"
}
}
};
new Vue({
el: "#app",
// 使用组件
template: `<App></App>`,
components: {
// 注册组件
App
}
})
- 从main.js 抽取到其他js文件中:
aaa.js:
// 导出组件
export default {
template: `
<div>
<h2>{{message}}</h2>
<button>按钮</button>
</div>
`,
data() {
return {
message: "Hello webpack"
}
}
};
main.js导入aaa.js:
// 引用vue
import Vue from 'vue'
// 导入aaa.js中的App
import App from './js/aaa'
new Vue({
el: "#app",
template: `<App></App>`,
components: {
App
}
})
-
最终方案— .vue文件创建组件:
5.1 创建vue组件目录和App.vue文件:
5.2 .vue文件包含三部分,template中写组件模板;script中写组件代码;style中写组件样式:
将aaa.js中定义的App组件迁移到App.vue中,组件的内容写在对应模块。
5.3 在main.js中导入App.vue组件:// 引用vue import Vue from 'vue' // 导入App.vue组件 import App from './vue/App.vue' new Vue({ el: "#app", template: `<App></App>`, components: { App } });
5.4 重新打包,报错需要loader编译.vue文件:
5.5 安装vue-loader和vue-template-compiler:
执行: npm install vue-loader vue-template-compiler --save-dev
5.6 配置webpack.config.js文件:{ test: /\.vue$/, use: ['vue-loader'] }
5.7 重新打包,报错vue-loader需要其他插件:
5.8 这是因为安装的vue-loader是15.9.1版本,14版本以上还需要安装其他插件,这里先把版本改为13.0.0,修改package.json后执行npm install重新安装:
5.9 重新打包:
5.10 如果想导入vue组件时,省略.vue后缀,需要配置webpack.config.js:
webpack中使用插件Plugins
loader和plugin区别
loader主要用于转换某些类型的模块,它是一个转换器。
plugin是插件,它是对webpack本身的扩展,是一个扩展器。
plugin的使用过程:
- 通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
- 在webpack.config.js中的plugins中配置插件。
添加版权的Plugin
- 配置web.config.js,导入webpack,配置plugins参数:
- 重新打包,查看bundle.js,已经包含了设置的版权信息:
打包html的plugin
- 当前index.html文件还是是存放在项目的根目录下的。但是,在真实发布项目时,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js等文件也就没有意义了。
所以,需要将index.html文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件。 - HtmlWebpackPlugin插件的作用:
① 自动生成一个index.html文件(可以指定模板来生成)
② 将打包的js文件,自动通过script标签插入到body中 - 安装HtmlWebpackPlugin插件:
执行:npm install html-webpack-plugin@3.2.0 --save-dev - 在webpack.config.js配置HtmlWebpackPlugin插件:
5.重新打包,dist目录下已经包含了index.html,并且添加script标签对bundle.js的引用:
- 如上,存在两个问题:
① 当前index.html已经在dist目录下, 引用bundle.js时不需要再加dist/。有这个dist目录的原因是,最开始index.html还在src目录时,生成的图片引用时没有加dist目录,解决办法是在webpack.config.js中添加了publicPath,现在index.html已经打包到了dist目录,只需要把publicPath删掉即可。
② 没有定义 div id=“app”。
在配置webpack.config.js中的HtmlWebpackPlugin配置template参数,指定按照哪个html来生成index.html。这里指定同级目录下的index.html。
另外,由于会自动加上bundle.js的引用,所以原index.html中的script可以删掉。重新打包,运行dist下新生成的inde.html。
压缩JS插件
在项目发布之前,需要对js等文件进行压缩处理,对打包的js文件进行压缩使用一个第三方的插件uglifyjs-webpack-plugin,并且版本号指定1.1.1,和CLI2保持一致。
- 安装:
执行命令:npm install uglifyjs-webpack-plugin@1.1.1 --save-dev - 配置webpack.config.js,导入uglifyjs-webpack-plugin,重新打包:
搭建本地服务器
webpack提供了一个可选的本地开发服务器,基于node.js搭建,内部使用express框架,可以实现让浏览器自动刷新显示代码修改后的结果。
不过它是一个单独的模块,在webpack中使用之前需要先安装它。
这个服务器是把打包的文件加载在内存中,并不会刷新到磁盘中。
-
安装webpack-dev-server:
执行命令: npm install --save-dev webpack-dev-server@2.9.1 -
配置dev-server:
devserver也是作为webpack中的一个选项,选项本身可以设置如下属性:
contentBase:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist。
port:端口号(默认8080)。
inline:页面是否实时刷新。
historyApiFallback:在SPA页面中,依赖HTML5的history模式。
-
配置启动命令:
在package.json中配置dev指令,然后通过npm run dev启动服务。
拆分开发和生成环境配置
有些配置是开发环境需要的,有些是生成环境才需要的。如下,devServer是开发环境才需要的,uglifyjsWebpackPlugin是生成环境才需要的。
-
新建build目录:
创建base、prod、dev.config.js文件,分别对应公共配置、生成环境配置、开发环境配置,并把对应配置抽离到对应文件中:
-
安装webpack-merge:
执行命令: npm install webpack-merge --save-dev -
使用webpack-merge合并配置文件:
prod.config.js:const uglifyjsWebpackPlugin = require("uglifyjs-webpack-plugin"); // 导入webpack-merge模块 const webpackMerge = require("webpack-merge"); // 导入baseConfig const baseConfig = require("./base.config"); // 合并 module.exports = webpackMerge(baseConfig, { plugins: [ new uglifyjsWebpackPlugin() ] });
dev.config.js:
// 导入webpack-merge模块 const webpackMerge = require("webpack-merge"); // 导入baseConfig const baseConfig = require("./base.config"); // 合并 module.exports = webpackMerge(baseConfig, { devServer: { contentBase: "./dist", inline: true } });
- 自定义配置文件路径:
删除根目录的webpack.config.js文件,在package.json中配置配置文件路径:
- 自定义配置文件路径:
-
修改打包输出路径:
之前webpack.config.js在根目录,其中配置了打包输出路径为“webpack.config.js拼接dist目录”。由于现在配置文件已经放在build目录,所以需要修改路径,否则会打包在build/dist目录。