近况
最近主要学习vue,在网课中使用到了webpack,在实操过程中深受webpack配置的折磨,太痛苦了
想着,如果不记录下来,写一篇webpack博客,对不起我吐血找bug。
本篇主要讲如下内容:
- 模块化
- 模块化之ES6语法及CommonJS语法
- webpack概念
- 安装webpack
- webpack的基础使用
- loader的使用
- webpack配置vue
- vue配置项目别名
- vue中el与template的关系
- vue的终极方案
- plugin的使用
- 搭建本地服务器(WDS)
- 配置文件的分离
- 完结
模块化
在讲webpack之前,我想先说说什么是模块化,因为webpack是一个前端模块化打包工具,如果不知道什么是
模块化,那就不要说什么webpack了。ok,下面进入正题
模块化指的是将一个js文件看成一个模块,实际上,不止是js文件,你可以把css样式,图片都看成是一个个模块
通过组织这些模块从而构建我们的项目。那我们来看看怎么组织这些模块(js,css,图片等等)。
组织模块说的是:在js文件中,使用一些别人提供的js语法,使得我们可以使用其他模块,做到模块间的数据共享
请看下面代码:
//创建hello.js,main.js,index.html,代码如下:
//hello.js
'use strict';
let say = function (name) {
console.log(`hello `+name);
}
export {say};
//main.js
import {say} from './hello.js';
say('万一');
//index.html
<body>
<script src="main.js" type="module"></script>
这里是default.html
</body>
/*
以上代码,当我们在浏览器打开index.html页面的时候,按f12,控制台会输出“hello 万一”
上面的代码其实讲的就是模块之间的组织,下面来分析一下上面的代码
在上面的代码中,hello.js文件(模块)中有一个say函数,然后使用export {say},将该函数暴露出去了
以后谁导入该hello模块,谁就可以使用这个say函数,可以看到,在main.js文件(main模块)中,
使用import {say} from './hello.js';导入了hello模块,拿到的say变量,其实就是say函数
然后直接就可以使用,最后在index.html页面中,引入main.js,并且type="module",当我们打开
index.html后,就可以看到控制台输出hello 万一。
以上这种export,import语法是es6语法,浏览器提供支持的,是模块之间相互联系的语法。
当然还有其他语法也是类似的,例如node.js中使用的CommonJS语法和ES6也是类似的。
*/
模块化不仅可以实现模块之间数据的共享,还可以实现模块之间变量作用域隔离,什么意思呢?
在以前,我们在一个页面引用两个js文件,这两个js文件之间的变量是不允许有重名的,
因为这些变量属于全局变量。而在模块中可以定义同名的变量,相互不影响,因为模块中的代码都被一个函数
包裹住(立即执行函数),所以在模块中定义的变量都是局部变量,自然不影响其他模块。
模块化之ES6语法及CommonJS语法
ES6模块化
上面的例子中是一个简单的ES6模块化语法,下面是一些细节export导出
//第1种导出方式就和上面案例一样,导出不仅可以导出变量,还可以导出函数,对象,类 let name='万一'; let you="明月"; function say() { console.log(`万一爱明月`); } export {say,name,you}; //第2种导出方式,直接在export后定义变量 export let name = '万一' //第3种导出方式,默认导出 let you="明月"; export default you; /* 注意:默认导出这种方式可以在导入时自定义变量名,其他方式导入时只能使用导出时的变量名 并且一个模块只能有一个默认导出 */
import导入
/* 第1种导入方式,和上面案例一样,注意需要有{}包裹变量名,对应导出的第1,2种方式 注意,这里的路径是相对路径,变量名不可随意取,导出时什么名字,导入时就是什么名字 */ import {say,xxx,xxx,xxx} from './a.js'; /* 第2种导入方式,通配符导入,当需要导入的功能太多的时候, 我们可以使用*表示导入该模块全部的导出的内容,并可以使用as取一个别名 该命令表示将a.js中导出的内容,全部导入到本模块中,并存到xxx对象之中,当需要某个功能时, 直接xxx.变量名,即可取出a.js中导出的变量名。 */ import * as xxx from './a.js' /* 第3种导入方式,默认导入,和默认导出一起使用的。与其他导入方式不同的是, 这里导入的变量并不需要大括号{}包裹,并且变量名随意取 */ import yang from './a.js';
CommonJS
CommonJS的导入导出需要node环境的支持,下面是导入导出语法导出: module.exports= { flag:true, test(a,b){ return a+b; }, demo(a,b){ return a*b; } } 导入: //解构赋值 let {test,demo,flag} = require('./moduleA'); //等同于 let mA = require('moduleA'); let test = mA.test; let demo = mA.demo; let flag = mA.flag; /* 注意:与ES6不同的是,导入模块时,模块的后缀名可以省略, 而在es6中不可以,require('./moduleA'); */
webpack概念
webpack是一个前端模块化打包工具,模块化我们知道了,打包又是个什么东西呢?
我们可以想象,在一个页面之中导入非常多的js文件,并且js文件之间又有逻辑关系,不能随意导入
而打包是将我们编写的js文件都打包成一个js文件,然后在页面只需要引用这一个js文件就行。
webpack还可以对图片进行压缩,将scss,less转成javascript,将es6语法转成es5语法,
将typeScript转成javascript等等很多的功能,所以它就是一个前端开发工具。
安装webpack
node和npm的概念
webpack的安装需要node环境,需要npm进行安装,node.js并不是一门开发语言,它定义了一套规则,
语法使得js可以运行在后端服务器上,所以可以做之前不能做的事情,例如可以使用js操作文件,数据库,网络等
这是node赋予js的能力,而npm,则是node包管理器,node package manage,帮助我们下载各种依赖包npm的一些小概念和命令
在官网下载好node之后,配置好环境变量(自动配置),npm是随着node一同下载的
**npm安装依赖分为全局安装和局部安装(本地安装)**指的是把依赖放在哪里,或者说依赖的作用范围。- 全局安装指的是将依赖包安装在node根目录的node_globalnode_module中,这个路径可以设置
使用命令:npm config ls
查看npm配置信息,查看npm全局安装的路径:npm get prefix
当我们使用npm命令安装依赖时加上-g,就为全局安装,否则为局部安装。 - 而局部安装指的是,当我们对一个文件夹进行初始化后,该文件夹就被npm管理起来了,被当做一个项目
对文件夹初始化的命令是:npm init
,执行命令之后会在该文件夹中生成一个node_module文件
和一个package.json文件,新生成的node_module文件夹存放局部安装的依赖包,
package.json文件则是该项目的一些依赖信息等等。
依赖又分为开发时依赖(又叫编译期依赖),运行时依赖
- 开发时依赖:只需要在开发时使用的依赖,例如图片压缩,es6转es5等等,项目打包后,
运行时并不需要这些依赖,安装开发时依赖在npm命令上加上–save-dev就可以把依赖信息
写到package.json中的devDependencies中,表示这是开发时依赖 - 运行时依赖:在程序运行时需要的依赖,例如vue等,安装运行时依赖在npm命令加上–save
就可以把依赖信息写到package.json中的dependencies中,表示这是运行时依赖。
npm安装依赖的两种方式
- 通过编辑项目中的package.json文件,编辑dependencies,devDependencies,
再执行npm install
命令。就会使得所依赖的module加载到项目的node_modules文件夹中。 - 不用package.json,而是直接通过npm install moduleName的方式,逐个添加依赖。
npm的一些常用命令
- 安装依赖:
npm install 依赖包名@版本号
,加上-g
则表示全局安装,
加上--save-dev
表示这是开发时依赖,会把依赖信息写到项目中的package.json的devDependencies中
如果不指定版本号,则安装最新版的依赖 - 卸载依赖:
npm uninstall 依赖包名@版本号 依赖包名@版本号
,可以安装卸载多个依赖 - 查看npm管理的依赖:在项目根目录中执行
npm list
,查看的是项目的局部安装依赖(或者叫本地安装)
查看全局安装的依赖:npm list -g --depth 0
,–depth 0:只显示第一层,不展开依赖项 - 重新安装项目依赖:在项目根目录中使用
npm install
,则会根据项目的package.json
重新安装所有依赖,在修改package.json文件后,都需要执行一下该命令。 - 查看全局安装存储的路径:
npm get prefix
查看npm配置信息:npm config ls
- 全局安装指的是将依赖包安装在node根目录的node_globalnode_module中,这个路径可以设置
安装webpack和其脚手架
webpack当然也是npm中的软件包,npm几乎管理着大多数的依赖,工具等等,安装命令如下:npm install webpack@5.38.0 webpack-cli@4.7.0 --save-dev
,直接局部安装,作为开发时依赖。
webpack的基础使用
上面已经介绍了什么是webpack,以及如何安装webpack,接下来我们来看下如何使用webpack
首先使用webStorm或者其他开发工具创建一个空项目,再创建两个目录,一个dist目录
一个src目录,dist目录存放的是打包后的文件,src目录存放项目的文件,包括js,css,图片
上面说过,webpack可以将所有js,css等等打包成一个js文件,然后html页面引入这个文件即可使用
说的就是将src中所有内容都打包进dist目录中,当然,图片不被打包成js文件而是拷贝到dist目录中。另外再创建一个index.html页面,index.html页面不放在src中,而是放在项目的根目录中
因为我们只需要把js,css等等模块打包成一个js文件,然后html引入即可使用。值得说明的是,我们需要改变一个思想,一个使用vue开发的项目一般只有一个html页面,
这被叫做单页面富应用,即SPA页面,利用JavaScript根据后端数据动态的变换 HTML,
从而实现UI与用户的交互。下图是传统web和单页面应用区别:在上面,我们在项目根目录中创建好了dist目录,src目录,index.html,我们还是来实现讲模块化时候的例子
在main.js文件中引入其它js文件的函数,然后在main函数中调用该函数,之前是将该main.js引入html页面
然后打开浏览器查看控制台输出,而现在是使用webpack对main.js进行打包成dist目录的js文件,
再在html引入打包后的js文件,虽然相比之前多了一个打包的动作,但是main.js作为入口文件,
其他js文件只要在main.js中被导入了,都可以打包成一个js文件,而不像以前将很多js文件引入到html中。
具体做法:在src目录中创建js目录,在js目录中创建将被导入的mathUtil.js,在src根目录中创建main.js//mathUtil.js function add(a, b) { return a+b; } function cheng(a, b) { return a*b; } export {add,cheng}; //main.js,作为入口文件,导入mathUtil组件中函数并调用 import {add,cheng} from './js/mathUtil.js'; console.log(add(3, 2)); console.log(cheng(3, 2));
请注意,目前为止还没有使用webpack,首先,进入该项目根目录,npm初始化该项目,
会在项目根路径下生成node_modules目录和package.json文件,当我们局部安装(本地安装)依赖后,
npm就会将依赖下载到node_modules中,package.json则是npm管理我们项目的文件,
其中有一些项目的依赖的信息,以及可以配置一些script脚本,简化依赖的使用。
初始化完毕后,安装webpack依赖。//进入项目根目录,初始化项目 npm init /* 安装webpack及其脚手架依赖,没有-g表示本地安装,@指定依赖版本号 --sace-dev则会将依赖信息写到项目的package.json中 */ npm install webpack@5.38.0 webpack-cli@4.8.0 --save-dev
然后我们还需要在项目根目录中创建一个webpack的配置文件,
webpack.config.js,在这个配置文件中可以配置一些webpack的扩展功能,行为。
我们需要在webpack配置文件中配置出口入口路径,入口路径指的是入口文件的位置,
就是我们的main.js的路径,出口路径指的是,打包后的js文件放在哪个目录,
webpack会根据出口入口路径,找到main.js,任何在该文件被导入的模块,都会被打包
打包成我们指定的js文件,然后我们在html页面中引入该文件即可,下面是我的webpack.config.js配置信息//因为全局安装中存在vue,所以可以直接导入vue的path模块 const path = require('path'); //CommonJs语法导出 module.exports={ entry: './src/main.js', //打包入口,相对于webpack.config.js,main.js所在的位置 output:{ /* path是出口路径,指要把打包后的文件放在哪里,__dirname是path模块的变量,表示当前文件的绝对路径 该变量表示当前webpack.config.js的绝对路径,resolve函数表示将两个路径进行自动拼接 所以整体意思就是打包后文件放在webpack.config.js同级的dist目录中 */ path:path.resolve(__dirname,'./dist'), filename: 'bundle.js', //打包后的文件名 } }
配置完毕后,在根目录下执行命令:
webpack --mode development
进行打包操作,--mode development
,该参数是webpack新版本需要指定的,表示使用开发模式进行打包,
开发模式打包,打包后的文件不会被压缩,如果是production,生产模式打包,则打包后的文件会被压缩
打包后会在dist目录下生成bundle.js文件,然后在我们的index.html页面中引入该js文件即可下图是我的目录结构,总体思路很简单,webpack帮我们将入口文件main.js,打包成dist中的bundle.js文件
我们通过npm命令即可打包,只要模块在main.js中使用到,就可以被打包成bundle.js,往后只需要在html
中引入打包后的js文件,而不需要在html页面中多个js文件。
下图引用bundle.js时,不需要增加type="module"
案例并没有结束,我们知道,配置完webpack的配置文件,webpack.config.js文件,
使用命令webpack --mode development
,即可让webpack进行打包,但是这种打包命令并不好
因为后面我们的webpack命令会加很多参数,这就使得打包的命令变得很长,不方便,
所以我们可以在package.json中配置一个script,小脚本的意思,配置好后,使用npm运行小脚本
即可执行相应的webpack命令。下面是我的package.json配置{ "name": "studywebpack", "version": "1.0.0", "description": "study for webpack", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack --mode development", "prod": "webpack --mode production" }, "author": "", "license": "ISC", "dependencies": { "webpack": "^5.38.0", "webpack-cli": "^4.8.0" } }
可以看到scripts属性中,配置了dev和prod,dev表示开发模式打包,prod表示生产模式打包,
dev,prod这些属性名随便写,当我们在根目录下执行npm run dev
时,就会执行dev后面的命令.上面我们学习了webpack的基本使用,我们知道,webpack就是将多个JS文件,打包成一个JS文件,
在webpack处理的模块中(JS文件),可以随意使用各种模块化语法进行导入导出,因为webpack会处理解析JS
的模块化导入导出,但是webpack.config.js中的导出只能使用CommonJS语法,因为webpack依赖node环境
loader的使用
webpack可以做的是:打包和处理JS模块化
随便你在项目中用什么模块化语法,webpack帮你解析,不需要考虑环境问题,但是webpack也有很多不能做的
webpack只能处理JS代码,并不能处理css,图片,loader就是一些别人开发的依赖,可以帮助webpack处理这些
例如将less转成css,将es6语法转成es5,解析图片等等。
loader的使用过程:通过npm安装需要使用的loader,再在webpack.config.js中的module关键字下进行相应的配置
好,那么我们开始配置几个常见的loader,webpack地狱配置开启!
配置css的loader
配置css的loader指的就是在webpack管理的项目中使用css文件创建一个normal.css文件,
body{background-color: aqua;}
去官网的loaders中寻找css的loader,使用npm安装这两个loader,css-loader,style-loader
//我的webpack版本是5.38.0,webpack-cli版本是4.8.0,最好保持一致吧 npm install --save-dev css-loader@6.2.0 style-loader@3.2.1
安装好两个loader之后,配置webpack.config.js
const path = require('path'); module.exports={ entry: './src/main.js', output:{ path:path.resolve(__dirname,'./dist'), filename: 'bundle.js', }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] } ] } } //注意:use数组中,style-loader和css-loader顺序不能反,webpack加载配置从数组右到左加载
配置完毕后,此时webpack能处理css文件了,但是我们需要在入口文件中引用css模块,
只有在入口文件中导入模块,才能被webpack进行打包处理,所以我的main.js如下://测试模块化 import {add,cheng} from './js/mathUtil.js'; console.log(add(3, 2)); console.log(cheng(3, 2)); //测试css文件 import css from './css/normal.css'
最后执行打包命令,
npm run dev
,查看index.html即可查看到页面背景颜色产生变化了
配置less的loader
less文件是css的预加载文件,配置less的loader的意思是,让webpack认识less文件,并使用相应的loader
将less文件转成css文件,具体步骤与配置css一样,与less相同的还有scss文件。在css目录中创建special.less文件,该文件内容如下:
@fontSize: 50px; @fontColor: red; body{ font-size: @fontSize; color: @fontColor; }
然后安装less的loader,使用less-loader还需要less依赖,一起进行安装
npm install --save-dev less-loader@7.3.0 less@4.1.1
配置webpack.config.js文件
module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },{ 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 }] } ] }
配置完毕后,需要在入口文件中引入special模块,因为样式是字体样式,所以使用JS输出一个标签
//测试less import less from './css/special.less'; document.writeln("<h3>我giao</h3>")
最后打包运行:
npm run dev
,查看页面即可
配置图片的loader配置图片的loader,就是让webpack能够识别图片模块,对图片进行处理。
webpack处理图片有两种方式,可以在webpack.config.js中配置一个图片大小(limit属性),
超过这个大小的图片会被拷贝进dist目录,然后浏览器会通过url请求方式得到这张图片,
而小于这个大小的图片,则会解析成base64字符串发给浏览器,让浏览器自行解析。第一步,先在src中创建img目录,放上两张图片,然后npm安装图片相关的依赖
npm install --save-dev url-loader@4.1.1 file-loader@6.2.0 //不同版本的webpack安装不同版本的各种loader。
然后在webpack.config.js中配置相关loader
注意:如果你是webpack5版本以上,你可以使用asset方式配置,这种方式我没太懂,没去研究
你也可以仍然使用loader的方式配置,但需要设置一些属性,下面是两种方式的配置
asset的配置{ test:/\.(jpg|png|gif)$/, type:"asset", //解析 parser: { //转base64的条件 dataUrlCondition: { maxSize: 25 * 1024, // 25kb } }, generator:{ //与output.assetModuleFilename是相同的,这个写法引入的时候也会添加好这个路径 filename: 'img/[name][hash:6].[ext]', //打包后对资源的引入,文件命名已经有/img了 publicPath:'./' }, }
webpack5以上,使用file-loader时的配置:
{ test: /\.(png|jpg|gif|jpeg)$/, use: [ { loader: 'url-loader', options: { limit: 8192, /* name是文件名,也可以加个路径(img目录),另外[name],则是图片原文件名 [hash:8]则是生成一个随机8位hash数,防止图片重名, [ext]是原文件名的后缀 */ name:'img/[name].[hash:8].[ext]', //输出路径,这里路径是:path(上面配置的dist目录)+outputPath+name路径 // outputPath: './', // publicPath: 'dist/' /* 在url-loader内部封装了file-loader而file-loader在新版本中esModule属性默认为 true,即默认使用ES模块语法导致了引用图片文件的方式和以前的版本不一样,引入路径改变了,自然找不到图片。 */ esModule: false }, } ], /* 当在webpack 5中使用旧的assets loader(如file-loader/url-loader/raw-loader等) 和asset模块时,这可能会导致asset重复,所以你可能想阻止webpack5内置的asset模块的处理, 你可以通过将asset模块的类型设置为‘javascript/auto’来解决。 */ type: 'javascript/auto' }
如果你是webpack4或以下,你可以去除上面的esModule和type两个属性。
配置完毕后,要想让webpack认识这个图片是模块,就需要与入口函数联系起来,
当我们在css的属性中引用这张图片时,入口文件中只要引入css模块,则这张图片也会被当做模块。
下面,我们更改normal.css文件,背景换成这张图片body{ /*background-color: aqua;*/ background: url("../img/2.jpg"); }
安装完loader,配置好webpack.config.js,引入完图片后,使用命令打包,
打包完毕后查看页面背景是否成功显示图片。
配置es6转es5的loader
有些老版本浏览器可能不支持es6,所以,webpack可以通过配置loader,将es6转成es5,适配老版本浏览器使用npm安装babel-loader等依赖
npm install babel-loader@8.2.2 @babel/core@7.15.0 @babel/preset-env@7.15.0 --save-dev
配置webpack.config.js文件,配置完毕后,webpack在打包的过程中会将bundle.js文件中es6的语法换成es5的语法。
{ test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }
webpack配置vue
我们使用webpack工具,就是为了能够解决模块之间的依赖问题和使用打包功能,
来看看,如何使用webpack去管理vue吧
npm安装vue依赖,并且配置webpack.config.js
//安装vue依赖,vue依赖在项目打包后仍需要使用,所以是运行时依赖,没有-g则是本地安装 npm install vue@2.6.14 --save //配置webpack.config.js,在exports的对象上,加上resolve属性,具体如下 resolve: { alias: { 'vue$':'vue/dist/vue.esm.js' } }
在入口文件(main.js)中,创建vue实例
//导入vue模块 import Vue from 'vue'; const app = new Vue({ el:'#app', data:{ message:'万一' } });
在index.html页面中代码如下,打包完成后,打开浏览器就可以看到数据了。
<body> <div id="app"> <h3>{{message}}</h3> </div> <script src="dist/bundle.js"></script> //一定要放在最后 </body>
vue配置项目别名
当使用命令vue create 项目名
或者vue init webpack 项目名
创建项目名后,你可能需要给项目的目录配置
一些别名,这样在项目中使用路径时就不需要使用../../../
这种方式,而是直接使用配置好的views/xxx
或者components/xxx
,如果在template中img的src中使用路径,需要加上~
,例如:~assets/xxx/xxx
别名配置,你需要在项目目录下创建vue.config.js,并在其中配置如下设置:
module.exports = {
configureWebpack: {
resolve: {
/*配置项目路径别名*/
alias: {
'assets': '@/assets',
'common': '@/common',
'components': '@/components',
'network': '@/network',
'views': '@/views'
}
}
}
}
vue中el与template的关系
上面那种方式开发中并不会使用,例如并不会在index.html中的div中写任何代码,SPA页面,或者说单页面富应用
说的就是,一旦index.html页面确定下来了,就不会再更改,而是通过JS动态更换DOM元素的方式与用户交互
所以像上面例子div标签中的{{message}}
,不会这样直接写,会写在组件的template中
当一个组件同时存在el属性和template属性时,template中的内容将会在页面渲染的时候替换el管理的div标签
//例如:index.html如下
<body>
<div id="app">
</div>
<script src="dist/bundle.js"></script> //一定要放在最后
</body>
//main.js
new Vue({
el:'#app',
template:'<h3>{{message}}</h3>',
data:{
message:'万一'
}
});
当页面在渲染的时候,会将template的内容替换index.html中的div,所以实际显示下面这样
<body>
<h3>{{message}}</h3>
<script src="dist/bundle.js"></script> //一定要放在最后
</body>
这部分内容属于vue的知识点,所以单独写出来。
vue的终极方案
说了最初的在webpack中使用vue的方式,又说了el和template的关系,都是为了vue终极方案做铺垫
我们在这里将会对结合上面两种方式,看看实际开发中是怎么使用vue的。
首先我们得有一个简单的index.html页面
//根据上面例子,不难想象在main中肯定有vue实例的template替换掉这个div标签 <body> <div id="app"> </div> </body>
在我们的main.js入口文件中,代码如下
//vue终极方案 import Vue from 'vue'; import wanyi from './vue/wanyi' new Vue({ el:'#app', template:'<wanyi/>', //template中的内容会替换html页面中的div components:{ //这里是根组件的子组件,相当于wanyi:wanyi,使用es6的变量增强写法 wanyi } });
上面代码中,我们引入了
wanyi
组件,这是我们自定义的.vue文件,这个.vue文件可以包含template块
样式块,和script块,我们以后写组件都是这样写,html代码,样式,js代码都写在一个.vue文件中<template> <button @click="show" class="bt">{{message}}</button> </template> <script> export default { //默认导出对象,该对象是一个组件 name: 'wanyi', data(){ return{ message:'万一', } }, methods:{ show(){ console.log('万一挑一,我是唯一'); } } }; </script> <style scoped> .bt{ color: blue; } </style>
.vue文件写完后,在main.js被作为模块导入,组件被挂载到vue实例中(根组件),然后在template中使用组件标签,
最后会替换index.html页面中vue实例管理的div标签。现在我们还不能使用.vue作为模块,
因为webpack不认识这个文件,也不知道怎么解析该文件,所以我们还需要安装一些loader,
让webpack能够处理该.vue文件,就像不认识css文件,就安装相应的loader一样。/* 安装vue-loader等依赖,注意vue-template-compiler版本要和vue版本一致 并且vue-loader版本如果高了会需要你配置插件,如果低了打包的时候会报错(Cannot read property 'vue' of undefined) 报错的解决方法:如果项目中webpack升级到4^ 的话 升级下vue-loader到14^ 然后问题就没有了 */ npm install vue-loader@14.2.4 vue-template-compiler@2.6.14 --save-dev //安装完loader,配置webpack.config.js { test: /\.vue$/, use: ['vue-loader'] }
配置完毕后,打包后,在浏览器就可以看到效果。
plugin的使用
plugin,插件,通常是用于对某个现有的架构进行扩展webpack中的插件,
就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等
loader和plugin区别:
- loader主要用于转换某些类型的模块,它是一个转换器,它操作的是文件,将文件A通过loader转换成文件B
是一个单纯的文件转换过程,例如less转css等 - plugin是对webpack本身的扩展,是一个扩展器,丰富webpack本身,增强功能,针对的是在loader结束
之后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,监听webpack打包过程
中的某些节点,执行广泛的任务。
plugin的使用过程
- 1)通过npm安装需要使用的plugin(某些webpack已经内置的插件不需要安装)
- 2)在webpack.config.js中的plugins中配置插件
几种常见的插件配置
配置添加版权的plugin,BannerPlugin
该插件作用是在打包后的bundle.js文件中第一行生成指定的版本信息
因为该插件是webpack内置的模块,所以在webpack.config.js中导入webpack模块
再配置默认导出对象,增加plugin属性,代码如下:const webpack = require('webpack'); plugins: [ //plugin是导出对象的属性 new webpack.BannerPlugin('最终版权归万一所有') ]
配置完毕后,重新打包文件,然后打开bundle.js,查看第一行,就会显示我们的版权
配置打包html的插件,html-webpack-plugin
目前我们的项目的index.html文件是在项目的根目录的,而实际上,我们的dist文件会放到服务器上运行,
但是打包后的dist中并没有我们的index.html文件,所以该插件可以根据指定模板(就是我们的项目根目录的index.html)
自动生成一个index.html文件,还可以将打包的js文件,自动通过script标签插入到新生成的body中。使用步骤如下:安装插件,
npm install html-webpack-plugin@5.3.2 --save-dev
配置webpack.config.js文件
const htmlWebpackPlugin = require('html-webpack-plugin'); plugins: [ new htmlWebpackPlugin({ /* 用于生成dist/index.html的模板,模板中并不需要写script标签引入bundle.js, 插件会自动引用,另外这里是模板路径,相对webpack.config.js路径. 我这里index.html和webpack.config.js在同一目录中,所以直接写文件名 */ template: 'index.html' }) ] //有了该插件,我们的index.html中就不需要引入bundle.js,index.html如下,只有一个div <body> <div id="app"> </div> </body>
配置好后,执行打包命令后,会根据根目录的index.html模板在dist中生成新的index.html
配置压缩JS文件的插件
通过配置可以在打包过程中压缩bundle.js,不过新版本webpack有个production模式,
以production模式运行webpack,会自动对bundle.js进行压缩。在package.json中配置"dev": "webpack --mode development", "prod": "webpack --mode production"
如果是使用插件的方式进行压缩,使用步骤如下:
安装插件:
npm install uglifyjs-webpack-plugin --save-dev
配置webpack.config.js
const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin') plugins: [ new UglifyjsWebpackPlugin() //压缩bundle.js文件会导致你的版权插件失效,被删除掉。 ]
搭建本地服务器(WDS)
webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,
可以实现我们想要的:让浏览器自动刷新显示我们修改之后的结果,这个功能,可以通过在webpack.config.js
返回的对象中配置一个watch属性实现,watch属性值为true时,当我们修改文件后,会自动打包我们的文件。
通过插件的方式配置本地服务器,步骤如下
安装本地服务器依赖,webpack,webpack-cli,webpack-dev-server三个依赖之间版本有影响,不能随便安装
npm install --save-dev webpack-dev-server@4.0.0
注意:如果安装失败,webpack5.0很可能安装失败,你就需要换种方式安装该依赖
打开项目的package.json,在devDependencies中增加"webpack-dev-server": "^4.0.0"
再在命令行执行:npm install
,或者npm install devDependencies
.配置webpack.config.js,在导出对象中增加devServer属性
devServer: { //表示为哪个文件夹提供本地服务,默认是根文件夹,相对webpack.config.js的路径 contentBase: './dist', inline: true //是否实时监听文件改动 } //webpack新版本可能是下面这种 devServer: { //表示为哪个文件夹提供本地服务,默认是根文件夹,相对webpack.config.js的路径 watchFiles: './dist', liveReload:true //是否实时监听文件改动 }
我们还需要配置package.json中的script,因为如果我们在项目根目录中执行启动本地服务器命令
webpack-dev-server
则实际上寻找的是全局安装的依赖,所以我们需要将命令配置在package.json中的script中,这样就会去
本项目的node_modules中寻找依赖。package.json中script配置如下:"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack --mode development", "prod": "webpack --mode development", "server": "webpack-dev-server --mode development --open" //--open表示本地服务器启动后,自动打开dits目录下的index.html页面 }
配置好后,启动本地服务器:
npm run server
,当修改源文件后,不需要打包即可快速刷新结果
其并不更新dist文件,而是将修改的代码放在内存中,当多次修改完成后,关闭本地服务器,再打包即可。
配置文件的分离
配置文件的分离指的是,将原来webpack.config.js中的配置分开,开发的配置和生产的配置分开放。
本案例中,我们将webpack.config.js文件,分成三个文件,base.config.js,dev.config,prod.config.js
base.config.js文件内存放开发和生产模式中都需要的配置,而dev.config.js文件存放开发模式时需要的配置
prod.config.js存放生产模式时需要的配置,当开发的时候,我们使用base.config.js+dev.config.js文件,反之亦然
下面是配置文件分离步骤:
首先在项目根目录创建config,并创建好这三个配置文件,在将各个配置写到分开写到这些配置文件之前
我们需要一个依赖,这个依赖帮助我们,将base.config.js与另外两个文件之间产生关联,也就是说,
当我们使用dev.config.js配置文件进行打包时,使用依赖,将base.config.js与dev.config.js中的配置进行合并
安装合并依赖:npm install webpack-merge@5.8.0 --save-dev
安装好依赖后,我们需要在dev.config.js和prod.config.js中使用依赖,使得他们与base.config.js产生关联
下面是拆分后的三个配置文件的代码//base.config.js //因为全局安装中存在vue,所以可以直接导入vue的path模块 const path = require('path'); const htmlWebpackPlugin = require('html-webpack-plugin'); //Common.js语法导出 module.exports={ entry: './src/main.js', //打包入口,相对于webpack.config.js,main.js所在的位置 output:{ /* path是出口路径,指要把打包后的文件放在哪里,__dirname是path模块的变量,表示当前文件的绝对路径 该变量表示当前webpack.config.js的绝对路径,resolve函数表示将两个路径进行自动拼接 所以整体意思就是打包后文件放在webpack.config.js同级的dist目录中 */ path:path.resolve(__dirname,'./dist'), filename: 'bundle.js', //打包后的文件名 }, module: { rules: [ { //css的loader test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },{ //less的loader 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 }] },{ //文件上传的loader test: /\.(png|jpg|gif|jpeg)$/, use: [ { loader: 'url-loader', options: { limit: 8192, name:'img/[name].[hash:8].[ext]', // outputPath: './', //输出路径,这里路径是:path(上面的)+outputPath+name路径 // publicPath: 'dist/' /* 在url-loader内部封装了file-loader而file-loader在新版本中esModule属性默认为true 即默认使用ES模块语法导致了引用图片文件的方式和以前的版本不一样,引入路径改变了,自然找不到图片。 */ esModule: false }, } ], /* 当在webpack 5中使用旧的assets loader(如 file-loader/url-loader/raw-loader等) 和asset模块时,这可能会导致asset重复,所以你可能想阻止webpack 5内置的asset模块的处理, 你可以通过将asset模块的类型设置为‘javascript/auto’来解决。 */ type: 'javascript/auto' },{ //es6转es5的loader test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } },{ //vue-loader test: /\.vue$/, use: ['vue-loader'] } ] },resolve: { //vue配置 alias: { 'vue$':'vue/dist/vue.esm.js' } },plugins: [ new htmlWebpackPlugin({ /* 用于生成dist/index.html的模板,模板中并不需要写script标签引入bundle.js, 插件会自动引用 */ template: 'index.html' }) ], // mode: 'development', //设置webpack模式 // watch: true // 监听文件改动并自动打包 }
//dev.config.js let webpackMerge = require('webpack-merge'); let base = require('./base.config'); module.exports = webpackMerge.merge(base,{ //本地服务器的配置 devServer: { watchFiles: './dist', //表示为哪个文件夹提供本地服务,默认是根文件夹,相对webpack.config.js的路径 liveReload:true //是否实时监听文件改动 }, });
//prod.config.js const webpack = require('webpack') let webpackMerge = require('webpack-merge'); let base = require('./base.config'); module.exports = webpackMerge.merge(base,{ plugins: [ new webpack.BannerPlugin('最终版权归万一所有'), ], });
将原来的webpack.config.js文件区分开后,我们需要更改base.config.js文件中的出口路径,
将原来出口路径中的./dist
改成../dist
,因为我们此时的配置文件在项目根路径下的config目录中,而dist目录在外层目录。output:{ /* path是出口路径,指要把打包后的文件放在哪里,__dirname是path模块的变量,表示当前文件的绝对路径 该变量表示当前webpack.config.js的绝对路径,resolve函数表示将两个路径进行自动拼接 所以整体意思就是打包后文件放在webpack.config.js同级的dist目录中 */ path:path.resolve(__dirname,'../dist'), filename: 'bundle.js', //打包后的文件名 }
最后,我们需要修改package.json文件中的script,因为如果是原来的配置,则webpack会寻找webpack.config.js文件
这个文件是webpack的默认配置文件名,而我们现在已经不使用这个配置文件了,我们需要在执行打包命令上
使用自己的配置文件名,下面是更改后的package.json中的script,更改后,执行打包命令即可看到效果。"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack --mode development --config ./config/dev.config.js", "prod": "webpack --mode development --config ./config/prod.config.js", "server": "webpack-dev-server --mode development --open --config ./config/dev.config.js" }
完结
上面是目前学习的webpack的全部内容,本次实验的代码,已经上传至github,有需要可以查看,查看案例代码
写完这篇,下一步就是继续看vue视频了,10月份之前一定看完vue网课!
下面是一些大佬文章: