webpack的使用
webpack中的loader
loader的作用
-
在之前的案例中,主要是使用webpack来处理所写的JS代码,并通过webpack来自动处理他们之间的依赖。
-
但是在实际开发中并不只JS文件的处理,还会加载css文件、图片等,对于webpack本身来说,对于这些转化是不支持的。
-
所以就需要给webpack扩展对应的loader。
loader的使用
- 通过npm安装所需要的loader。(webpack中文官网)
- webpack.config.js中的modules属性下配置。
webpack的loader
css-loader 和 style-loader
当没有使用css-loader时,直接在main.js文件中引用依赖然后打包:
//normal.css
body {
background-color: pink;
}
//main.js
const {name,age,say,run} = require('./js/info.js');
console.log(name);
console.log(age);
console.log(say);
run();
//依赖CSS文件,使得webpack打包时会处理normal.css文件
require('./css/normal.css');
控制台会输出如下错误:
所以需要安装css-loader来处理css文件。使用npm install --save-dev css-loader
。
💥--save-dev
为开发时依赖模块,意为只用于开发,实际项目运行时并不依赖。
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
注: 如果只是安装css-loader并打包,在DOM中并不会显示出css文件定义的效果。因为css-loader只是将css文件加载,想要将加载好的css文件渲染到DOM中,还需要安装style-loader(
npm install style-loader --save-dev
);在使用
use: [ 'style-loader', 'css-loader' ]
这个语句时,应注意在使用多个loader时,loader的执行顺序是从右往左,所以应该将style-loader放在左边,先执行css-loader加载css文件之后,再由style-loader渲染到DOM中。
less-loader
Less 是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量、Mixin、函数等特性,使 CSS 更易维护和扩展。
less-loader用于处理JS文件中对less文件的依赖。
-
安装less-loader:使用指令
npm install --save-dev less-loader less
。⚠️注: 如需安装指定版本,需要在loader后加
@+版本号
。以下同理。 -
在webpack.config.js文件中配置(执行顺序是自下而上)。
{ test: /\.less$/, use: [{ loader: "style-loader" }, { loader: "css-loader" }, { loader: "less-loader", options: { strictMath: true, noIeCompat: true } }] }
url-loader
作用
使用url-loader
加载css样式中的一些图片等文件。
安装
使用指令:npm install --save-dev url-loader
。
使用
在webpack.config.js中配置:
{
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'url-loader',
options: {
//当文件小于limit所定义的数值时,会将图片编译成base64字符串形式。
//当图片大于limit定义的数值时,打包会报错,就需要使用file-loader模块进行加载。
limit: 13000
}
}]
}
🔊如果插入的图片过大,就需要使用 file-loader模块进行加载。
file-loader
用于处理大文件的加载.
-
安装指令:
npm install --save-dev file-loader
。 -
然后再次运行打包指令。
-
运行index.html,控制台会提示找不到文件。
-
是由于使用webpack打包时,webpack会把图片文件放到打包文件同一路径下直接引用,但是是直接引用,未指定前置路径。
同一路径 没有前置路径 -
在调试中将url路径改为
url(dist/8e08b79ef4c5e855db743e16c3b5f2a1.png)
,
此时页面就会正常渲染。 -
如果想要解决这个问题,就需要使background后的url引用路径改为
dist/xxx.jpg
。在webpack.config.js文件中加入publicPath: 'dist/'
。意为只要涉及到url的问题,都会自动地在文件前面加上dist/。
💡当index.html文件也在dist文件夹下, 则不需要再使用 publicPath: 'dist/'指令 |
关于生成的文件名
-
webpack会在打包时将文件放入打包文件的同一目录下,并且会生成一个32位的hash值,防止文件命名重复。
-
但是在实际的开发中,我们可能对打包的名字有一定的要求——比如,将所有的图片放在一个文件夹中,但是需要跟上原来的名字,同时还要防止重复。
-
此时我们需要在options中添加一个name属性:
img
:文件要打包的文件夹;name
:获取图片原来的名字,放在该位置;hash:8
:为了防止图片名称重复,依然使用hash,但是只保留8位;ext
:原来的图片扩展名。
//使用[name]就是使用图片原有的name,使用.是将名称连接起来 name: 'img/[name].[hash:8].[ext]'
-
将原本打包好的的文件删除,然后重新打包
npm run build
。
babel-loader
🏆我们上述的打包文件中会有ES6的语法,当浏览器不支持ES6时会无法正常渲染,所以我们需要使用babel-loader模块完成转换。
-
安装:
npm install babel-loader babel-core babel-preset-env webpack
-
配置:
{ test: /\.js$/, //由于最后打包完成并运行node依赖文件并不参加,所以使用exclude将文件排除 exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { // presets: ['@babel/preset-env'] // 为什么使用2015版本不知 presets: ['es2015'] } } }
-
再次运行
npm run build
。打包完成,然后进入打包好的文件bundle.js搜索ES6的语法(如let
,const
),没有搜到,说明已经成功将ES6的语法转化为ES5。
webpack配置Vue
Vue模块
安装
在之前的学习过程中,在项目中引用Vue大致是使用两种方法:
-
下载Vue.js文件,然后通过
<script>
标签引入;<script src="../js/vue.js"></script>
-
使用cdn引入
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
💥而这两种方法都是不符合我们的模块化开发思想的,模块化开发应该是以引入模块的方式进行使用。所以我们需要使用npm安装的方式来引入vue模块。
使用指令:
npm install vue --save
注:由于Vue在运行时也是需要依赖的,所以不需要使用–save-dev。
使用
-
在js文件中使用vue进行开发:
//使用vue进行开发 //导入vue模块,引号中的vue就是安装好的vue模块 import Vue from 'vue'; const app = new Vue({ el: '#app', data: { msg: 'hello webpack' } })
-
在index.html文件中输入代码并执行:
<div id="app"> <h1>{{msg}}</h1> </div> <script src="./dist/bundle.js"></script>
运行时的错误-模板编译器
-
并没有执行,浏览器会抛出一个错误:
🗨️这是由于vue模块有两个版本:
runtime-only
和runtime-compiler
:- runtime-only => 代码中不可以有任何的template;
- runtime-compiler => 代码可以有template,因为有
compiler
用于编译template。
🗨️因为使用的是前者打包,它其中并没有包含模板编译器,所以浏览器无法正常运行。被Vue实例el属性挂载的元素也可以看做是一个模板
-
解决这个问题需要在webpack.config.js文件中加入:
resolve: { //别名 alias: { // vue$表示以vue结尾的文件,由于默认情况下vue使用的是vue.runtime.js,所以我们需要将vue的默认使用改变从而使用runtime-compiler。 'vue$': 'vue/dist/vue.esm.js' } }
是将Vue的默认使用文件改变为vue.esm.js从而可以使用模板编译器 -
再次
npm run build
,就可以在浏览器中正常渲染啦!
创建Vue时el和template的关系
在上面的代码可以正常运行后,有一个问题出现了:
❓ 当我们后续想要添加或修改data或其他属性的引用方式,我们就需要在index.html中修改相应的代码。那么有没有一种方法可以使我们在js文件中就可以修改呢?
Vue实例中的template属性
答案是我们可以在Vue实例中定义一个template属性:
-
在之前,我们只是定义了el属性,使得Vue实例对html中一个元素进行绑定;
-
现在我们只在html中保留一个div#app的元素,然后在Vue实例中定义一个
template
属性;const app = new Vue({ el: '#app', template: ` <div> <h2>{{msg}}</h2> <h2>{{name}}</h2> <button @click="btnClick">点击事件</button> </div> `, data: { msg: 'hello webpack', name: 'coderbut' }, methods:{ btnClick(){ alert('alert'); } } })
-
完成之后即使html页面只有一个div#app元素,也可以正常渲染。有利于文件的分离。
分离写法
在上面,使用了Vue实例中的template属性,从而使前端html文件中只有一个div#app的元素。
我们可以定义一个子组件,将属性从Vue实例中分离出来:
const App = {
template: `
<div>
<h2>{{ msg }}</h2>
<h2>{{ name }}</h2>
<button @click="btnClick">点击事件</button>
</div>
`,
data() {
return {
msg: 'hello webpack',
name: 'coderbut'
}
},
methods: {
btnClick() {
alert('alert');
}
}
}
然后在Vue实例中注册模板,并在实例中的template
属性中使用组件。
new Vue({
el: '#app',
template: '<App />',
components:{
App
}
})
打包并运行,这样也可以得到相同的效果
模块化 - 在js文件中导出
✨除了上述在一个js文件中定义一个子组件,还可以在另一个js文件中定义并导出,然后在mian.js文件中导入。
-
定义一个App.js文件,并编辑。
//App.js //default表示导出一个模块并且由导入者命名 export default { template: ` <div> <h2>{{ msg }}</h2> <h2>{{ name }}</h2> <button @click="btnClick">点击事件</button> </div> `, data() { return { msg: 'hello webpack', name: 'coderbut' } }, methods: { btnClick() { alert('alert'); } } }
//main import Vue from 'vue'; import App from './js/App' new Vue({ el: '#app', template: '<App />', components:{ App } })
-
打包并运行,依然可以得到相同的效果。
模块化 - 在.vue
文件中配置
在使用vue模块中,我们可以定义后缀为.vue
的文件,在里面定义属性模板,之后在js文件中引用。
-
首先创建一个App.vue文件。
-
在vue文件中编辑。
<template> <div> <h2>{{ msg }}</h2> <h2>{{ name }}</h2> <button @click="btnClick">点击事件</button> </div> </template> <script> export default { name: "App", data() { return { msg: 'hello webpack', name: 'coderbut' } }, methods: { btnClick() { alert('alert'); } } } </script> <!--可以为模板内的元素定义样式--> <style scoped> h2 { color: powderblue; } </style>
-
在main.js文件中导入该文件,注意此时文件需要加后缀;
import App from './vue/App.vue'; mport App from './vue/App.vue'; new Vue({ el: '#app', template: '<App />', components:{ App } })
💡 如果不想导入的时候加后缀,可以在webpack.config.js文件中配置
resolve
属性resolve: { //该属性表示可以省略的后缀名 extensions:['.js','.css','.vue'] }
-
此时如果直接打包会报错;
-
我们需要安装vue-loader和vue-template-compiler。
npm install vue-loader vue-template-compiler --save-dev
然后在webpack.config.js文件中添加规则:
rules: [ { //意为 .vue 文件使用vue-loader处理 test: /\.vue$/, use: ['vue-loader'] } ]
🚨注:在vue-loader14版本之后,使用vue-loader需要另外配置一个插件,这次我们把vue-loader的版本改到13 。
-
更改vue-loader版本。
进入package.json文件,将vue-loader的版本改为
^13.0.0
。再次运行npm install
。 -
安装完成之后再次运行
npm run build
进行打包。 -
运行。
当然,在vue文件中也可以导入vue文件(套娃警告)。
-
定义另一个vue文件
Cpn.vue
;//Cpn.vue <template> <div> <h1>{{ msg }}</h1> </div> </template> <script> export default { name: "Cpn", data() { return { msg: 'cpn template' } } } </script> <style scoped> </style>
-
在App.vue中导入并注册;
components: { Cpn }
-
之后重新打包并运行,就可以看到我们在App.vue中使用的Cpn.vue的内容了。
webpack的plugin(插件)
什么是plugin?
🦆 plugin是插件的意思,通常是用于对某个现有的架构进行扩展;
🦆 webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。
plugin和loader的区别
👁️ loader主要用于转换某些类型的模块,它是一个转换器;
👁️ plugin是插件,它是对webpack本身的扩展,是一个扩展器。
plugin的使用
- 通过npm安装需要使用plugins(某些webpack内置的插件无需安装);
- 在webpack.config.js文件中
plugins
中配置插件。
webpack的各种plugins
BannerPlugin插件
作用
BannerPlugin是webpack的自带插件,用来为打包的文件添加版本声明。
具体使用
-
修改webpack.config.js文件(由于是自带插件,所以不需要安装):
-
首先导入webpack模块
//导入webpack const webpack = require('webpack');
-
然后在属性中添加
plugins
属性并定义plugins: [ new webpack.BannerPlugin('最终版权归coderbut所有') ]
-
-
重新打包,然后在打包好的bundle.js文件中就可以看到我们用BannerPlugin定义的内容了。
HtmlWebpackPlugin插件
目前我们的index.html是存放于根目录下的,在发布项目时,只是会发布dist
文件夹,但是如果该文件夹下没有index.html文件,那么打包好的js文件也就无法使用了,所以我们需要将index.html文件也打包到dist文件夹中,这时就需要使用HtmlWerbpackPlugin
插件了。
作用
▶️ 自动生成一个index.html文件(可以指定模板生成);
▶️ 将打包好的js文件,通过<script>
标签插入到<body>
中。
具体使用
-
安装插件(学习的时候安装3.2.0版本,高版本会报错)
npm install html-webpack-plugin@3.2.0 --save-dev
-
修改webpack.config.js中的
plugins
内容:plugins: [ new HtmlWebpackPlugin() ]
-
打包,之后就可以在dist目录下看到index.html文件,并且自动引入了bundle.js文件。
注: 由于现在index和bundle文件打包在同一目录下,所以我们不再需要使用webpack.config.js文件中的
publicPath
属性来指定路径。
UglifyjsWebpackPlugin插件
作用
压缩打包好的js文件。使js文件变得可读性差。
具体使用
-
安装插件(安装1.1.0版本,跟着老师做,先用指定版本~不然报错没法整,也是与cli的版本对应);
npm install uglifyjs-webpack-plugin@1.1.0 --save-dev
-
在webpack.config.js中配置;
-
引入
//引入UglifyWebpackPlugin插件 const UglifyWebpackPlugin = require('uglifyjs-webpack-plugin');
-
配置plugins
new UglifyWebpackPlugin()
-
-
再次打包,找到bundle.js文件查看。
webpack-dev-server搭建本地服务器
作用
webpack提供了一个可选的基于nodejs的本地开发服务器,内部使用express框架,可以使浏览器自动刷新显示我们修改之后的结果。
使用
-
使用npm安装该模块(跟着老师做,先用指定版本~不然报错没法整,也是与cli的版本对应);
npm install --save-dev webpack-dev-server@2.9.3
-
在熟悉的webpack.config.js文件中配置
devserver
属性:devServer: { //为哪个文件夹提供本地服务,默认根文件夹 contentBase: './dist', //是否实时监听 inline: true //端口号,先不配置了 //port: }
-
再去package.json中配置一个
scripts
;"dev": "webpack-dev-server --open" // --open是打开浏览器
-
然后再运行
npm run dev
,会启动监听并打开浏览器,此时的路径就为 -
实时监听效果
webpack配置文件分离
为什么这样做
在webpack.config.js文件中,我们有的配置是需要在开发时配置的,而实际运行时并不需要,比如WebpackDevServer模块、各种plugin。
具体使用
-
分离我们的配置文件,定义三个配置文件。
- base.config.js:用于存储开发和运行公用的配置,即这里面的配置既是开发时依赖,也是运行时依赖;
- dev.config.js:开发时特殊的配置放在dev配置文件里面,只是开发时需要,实际运行时不需要的配置;
- prod.config.js:生产时(运行时)特殊的配置放在prod配置文件里面。
//base.config.js const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/main.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { test: /\.vue$/, use: ['vue-loader'] } ] }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' } }, plugins: [ new webpack.BannerPlugin('最终版权归coderbut所有'), new HtmlWebpackPlugin({ template: 'index.html' }) ], }
//dev.config.js module.exports = { devServer: { contentBase: './dist', inline: true } }
//prod.config.js const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin'); module.exports = { plugins:[ new UglifyjsWebpackPlugin() ] }
-
安装
webpack-merge
模块(还是低版本,教程比较老,为了学习~)npm install webpack-merge@4.1.5 --save-dev
之后在各个文件中导入并使用
//prod.config.js //导入WebpackMerge和base.config.js const WebpackMerge = require('webpack-merge'); const BaseConfig = require('./base.config') const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin'); //使用WebpackMerge //意为将第二个参数里的数据和导入的base.config.js文件合并 module.exports = WebpackMerge(BaseConfig,{ plugins:[ new UglifyjsWebpackPlugin() ] });
const WebpackMerge = require('webpack-merge'); const BaseConfig = require('./base.config'); module.exports = WebpackMerge(BaseConfig,{ devServer: { contentBase: './dist', inline: true } });
-
删除webpack.config.js文件,之后运行打包程序,终端会报错;
-
此时我们需要在package.json中的
script
属性中配置,为其中的自定义指令指令添加额外的命令;//自定义配置文件 "build": "webpack --config ./build/prod.config.js", "dev": "webpack-dev-server --open --config ./build/dev.config.js"
-
再次打包,就可以成功了。
-
但是仍然会存在一个问题,就是在打包成功之后,打包好的bundle.js文件会在build文件下新建的dist文件夹中。
解决这个问题只需要进入base.config.js文件,找到当时定义的
path
属性,然后修改。entry: './src/main.js', output: { //找到上一层然后拼接dist path: path.resolve(__dirname, '../dist'), filename: 'bundle.js', }
修改完成之后再次打包,打包好的文件就会出现在该出现的位置了(根目录下的dist文件夹)。