js
,css
,html
代码的过程ts
—>js
,less
/scss
—>css
等)js
,css
,html
代码;压缩合并图片等)注:若是你是初学者,但愿你能跟着个人步骤操做一下css
npm install webpack --save-dev
)webpack-cli
(npm install webpack-cli --save-dev
),以便解析用户传参(核心yargs
)src
文件夹并在其中建立index.js
文件
// index.js
console.log('hello webpack')
复制代码
npx webpack
main.js
文件的dist
文件夹;同时你也必定注意到了如上图中红框所圈出的WARNING
,是由于咱们没有指定mode
(模式:包含生产(production
)和开发(development
)两种),不传会默认production
模式npx webpack --mode development
WARNING
,比较一下此时main.js
和以前的区别src
文件夹中再建立一个other.js
,在index.js
经过require
引入// other.js
module.exports = 'hello webpack'
// index.js
const other = require('./other.js')
console.log(other)
复制代码
npx webpack --mode development
dist
文件夹下建立一个index.html
文件并引入产生的main.js
文件,在浏览器中查看index.html
的console
index.html
中直接引入src
文件夹下的index.js
,在浏览器中查看index.html
的console
后者的缘由是由于咱们的浏览器不支持
coomonjs
,这里我不作过多解释;咱们来分析一下前者是如何实现的html
// 删除掉冗余部分的main.js文件
(function (modules) {
var installedModules = {};
function __webpack_require__(moduleId) { // ./src/index.js ./src/other.js
if (installedModules[moduleId]) { // 相似Commonjs中的缓存处理
return installedModules[moduleId].exports;
}
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true;
return module.exports;
}
return __webpack_require__("./src/index.js");
})
({
"./src/index.js":
(function (module, exports, __webpack_require__) {
// 默认会调用 require("./src/other.js")
eval("const other = __webpack_require__(\"./src/other.js\");console.log(other);");
}),
"./src/other.js":
(function (module, exports) {
eval("module.exports = 'hello webpack';");
})
});
/* webpack打包流程 * 1.找到入口文件(index.js) * 2.找到入口文件中的依赖模块(other.js) ast解析加载依赖模块 * 3.从入口文件开始执行 require方法改写成 __webpack_require__ * 内部本身实现了一个Commonjs模块(须要找到入口,须要找到入口中的全部依赖,加载依赖 + 模板——>渲染后的结果) */
复制代码
真正的开发中咱们确定不会零配置打包,确定根据咱们的须要来手动配置
webpack
node
webpack.config.js
或webpackfile.js
文件。(本文以webpack.config.js
为例)
webpack.config.js
(采用coomonjs
规范)// webpack.config.js
module.exports = { // 导出一个对象;对象中放置配置;会调用这个配置进行构建
}
复制代码
src
文件夹下index.js
文件名为guYan.js
,执行npx webpack --mode development
,报错显示找不到入口文件
webpack.config.js
文件中增长入口的位置,并执行npx webpack --mode development
;无报错// webpack.config.js
module.exports = {
entry: './src/guYan.js' //当前入口文件的位置
}
复制代码
注:咱们能够经过执行
npx webpack --mode development
时增长config
参数来实现修改默认配置文件webpack.config.js
的名字。好比咱们如今修改webpack.config.js
文件名为webpack.guYan.js
而后执行npx webpack --mode development --config webpack.guYan.js
效果和以前同样react
webpack.config.js
文件中增长出口文件的配置,并执行npx webpack --mode development
,效果如图// webpack.config.js
const path = require('path');
module.export = {
entry: './src/guYan.js',
output:{ // 配置当前出口文件
filename: 'bundle.js', // 出口文件名
path: path.resolve(__dirname,'./build') // 出口目录,该配置项必须是绝对路径,默认dist目录
}
}
复制代码
HTML
模板public
目录,并在其中建立index.html
文件
html-webpack-plugin
插件(npm install html-webpack-plugin --save-dev
)
html
webpack.config.js
文件中增长HTML
模板的配置(使用html-webpack-plugin
插件)// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');// 1.引入插件
module.exports = {
entry:'./src/guYan.js',
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'./build')
},
plugins:[ // 2.在plugins中注册插件
new HtmlWebpackPlugin({
template:'./public/index.html', // 指定模板
filename:'gy.html' // 指定打包后的文件名,默认为index.html
})
]
}
复制代码
npx webpack --mode development
,输出文件目录如图,而且你会发如今输出的gy.html
中自动引入了打包后的bundle.js
npx webpack --mode production
会发现打包后的bundle.js
被压缩,可是gy.html
没有被压缩,还有就是引用的文件一直是一个咱们不清楚会不会出现缓存的问题,这上线确定是不容许的啊,因此咱们须要继续配置// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.export = {
entry:'./src/guYan.js',
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'./build')
},
plugins:[
new HtmlWebpackPlugin({
template: './public/index.html',
filename:'gy.html',
minify:{ // 配置压缩形式
removeAttributeQuotes: true, // 删除全部属性的引号
collapseWhitespace: true // 删除全部空白
},
hash:true // 每次引用js文件都加入哈希戳
})
]
}
复制代码
npx webpack --mode production
后对比以前的效果查看mode
(模式)mode
参数,其实没有必要这么麻烦,咱们能够在webpack.config.js
中配置mode
;执行npx webpack
结果和上文同样// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports= {
mode: 'production', // 默认两个参数 development production
entry: './src/guYan.js',
...
}
复制代码
clean-webpack-plugin
(清理文件夹)在开发构建的过程当中咱们更但愿在打包后的文件夹中一直是最新的打包文件,即先删除掉以前打包产生的文件夹及文件webpack
clean-webpack-plugin
(npm install clean-webpack-plugin --save-dev
)webpack.config.js
文件中进行配置// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clearn-webpack-plugin'); // 1.引入插件
module.exports = {
mode:'development',
entry: './src/guYan.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname,'./build')
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'gy.html',
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true
},
hash:true
}),
/*清空文件的插件*/
new CleanWebpackPlugin({ // 2.注册插件
cleanOnceBeforeBuildPatterns:['**/*'], // 匹配目录下的全部文件清空,默认也是全部
})
]
}
复制代码
webpack.config.js
中的entry
的值都是一个路径字符串的格式('./src/guYan.js'
),当咱们的入口只有一个的时候咱们这样写ok,可是当咱们须要同时打包多个文件的时候呢?咱们如何处理呢?对的,它的值还能够是个对象guYan2.js
// src/guYan2.js
console.log('I am GuYan');
复制代码
webpack.config.js
中修改咱们的配置,并执行npx webpack
打包后的文件夹变化效果// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode:'development',
entry:{ // 配置多入口
index:'./src/guYan.js',
other:'./src/guYan2.js'
},
output:{
filename: '[name].js',// 为了响应多入口出口文件名须要动态建立,保证每一个入口都有输出
path: path.resolve(__dirname,'./bundle')
},
plugins:[
new HtmlWebpackPlugin({
template:'./public/index.html',
filename:'index.html',
minify:{
removeAttributeQuotes:true,
collapseWhitespace:true
},
hash:true
}),
new CleanWebpackPlugin({
clearnOnceBeforeBuildPatterns:['**/*']
})
]
}
复制代码
html
文件引入打包后的js
文件不出现缓存给咱们形成的困扰,咱们在使用html-webpack-plugin
的时候配了一个hash
,咱们也能够在出口文件名的位置加入[contentHash]
参数// webpack.config.js
...
output:{
filenama:'[name].[contentHash:8].js' // :后面的是表明长度
}
...
复制代码
js
文件一个html
文件中引用了两个js
文件,那若是咱们想分别引用呢?怎么处理?对的,再new
一次html-webpack-plugin
。因此咱们写一个循环来处理一个plugin
须要屡次引用的问题// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
let htmlPlugins = ['index','other'].map(chunkName=>(new HtmlWebpackPlugin({
filename:`${chunkName}.html`,
chunks:[chunkName] // 保证按需引入 即index.html文件只引入index.js;other.html只引入other.js
})));
modeule.exports = {
mode:'development',
entry:{
index:'./src/guYan.js',
other:'./src/guYan2.js'
},
output:{
filename:'[name].[contentHash:8].js',
path:path.resolve(__dirname,'./build')
},
plugins:[
new CleanWebpackPlugin(),
...htmlPlugins
]
}
复制代码
devServer
(webpack-dev-server
)在开发过程当中咱们更但愿的是咱们一边修改代码页面自动更新,固然这种处理咱们有两种方案一个是经过编译的时候传入
--watch
不推荐使用(因此我就不作介绍了),另外一种就是经过webpack-dev-server
来启动,这种方式不会产生构建好的文件,而且会给咱们启动一个服务css3
注:本小节的例子咱们采用单入口输入,示例以下web
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
let htmlPlugins = ['index'].map(chunkName=>(new HtmlWebpackPlugin({
filename:`${chunkName}.html`,
chunks:[chunkName]
})));
module.exports = {
devServer:{
port:3000, //指定端口号
open:true, // 是否自动打开浏览器
compress:true, // 是否启用gzip压缩
contentBase:'./dist' // 指定启动静态服务的文件目录
// 经过webpack-dev-server打包的结果放到内存中,并且目录是当前的根目录
},
mode: 'development',
entry: {
index: './src/guYan.js',
},
output: {
filename: '[name].js',
},
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ['**/*']
}),
...htmlPlugins,
]
}
复制代码
npx webpack-dev-sever
启动来查看结果loader
webpack
只能理解 JavaScript
和 JSON
文件。loader
让 webpack
可以去处理其余类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。enforce:'pre'
)、普通(enforce:'normal'
)、后置(enforce:'post'
)css
style
文件夹,并在其中建立一个index.css
文件,并在src/guYan.js
中引入
/*index.css*/
body{
background:yellow;
}
复制代码
css-loader
,style-loader
(npm install css-loader style-loader --save-dev
)
css-loader
将css
文件处理成字符传形式,经过style-loader
在html
中建立<style>
标签并将css-loader
处理后的字符串插入其中webpack.config.js
中配置loader
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
let htmlPlugins = ['index'].map(chunkName=>(new HtmlWebpackPlugin({
template:'./public/index.html',
filename:`${chunkName}.html`,
chunks:[chunkName],
minify:{
removeAttributeQuotes:true,
collapseWhitespace:true
},
hash:true
})))
module.exports = {
mode:'development',
devServer:{
port:3000,
open:true,
compress:ture,
contentBase:'./dist'
},
entry:{
index:'./src/guYan.js'
},
output:{
filename:'[name].js'
},
plugins:[
new CleanWebpackPlugin(),
...htmlPlugins
],
module:{
rules:[
{
test:/\.css$/,
use:['style-loader','css-loader']
}
]
}
}
复制代码
npx webpack-dev-server
结果以下
CSS
相关css-modules
common.css
文件中,而后在咱们js
文件中按需引入;这样咱们就会遇到css
模块化的问题(css-modules
),咱们举例说明如何配置解决这个问题src/guYan.js
,在style
目录下建立一个common.css
文件// guYan.js
import { other } from './other';
import '../style/index.css';
import commonStyle from '../style/common.css';
let div = document.createElement('div');
div.className = commonStyle.guYan;
div.innerHTML = 'GuYan';
document.body.appendChild(div);
console.log(other);
复制代码
/*common.css*/
.guYan{
color:red;
}
复制代码
webpack.config.js
// webpack.config.js
...
module:{
rules:[
{
test:/\.css$/,
use:['style-loader',
{
loader:'css-loader',
options:{
modules:true
}
}
]
}
]
}
...
复制代码
npx webapck-dev-server
特殊注意:
loader
的顺序必定要特别注意,若是就想把style-loader
写在后面须要在其options
中增长enforce:'post'
参数或者在css-loader
的options
中增长enforce:'pre'
参数npm
css3
增长浏览器内核前缀style/common.css
以下/*common.css*/
.guYan{
color: red;
background: green;
transform: rotateY(180deg);
}
复制代码
postcss-loader
(npm install postcss-loader --save-dev
)postcss-loader
只是一个干活的,它不知道干什么活,因此须要一个东西来告诉它,它须要作的是增长前缀,这个东西就是autoprefixer
,因此咱们安装autoprefixer
(npm install autoprefixer --save-dev
)postcss-loader
)有了,指挥干活的(autoprefixer
)也有了,可是呢这个干活的(postcss-loader
)比较装X,它要求这个指挥干活的(autoprefixer
)不能像其余loader
那样你随随便便在options
对象中指挥我,你必须专门在个人配置文件(postcss.config.js
)中指挥我干活,因此咱们须要在根目录下建立一个postcss.config.js
文件postcss.config.js
文件// postcss.config.js
module.exports = {
plugins:[
require('autoprefixer')
]
}
复制代码
webpack.config.js
// webpack.config.js
...
module:{
rules:[
{
test:/\.css$/,
use:[
'style-loader',
{
loader:'css-loader',
options:{
modules:true
}
},
'postcss-loader'
]
}
]
}
...
复制代码
npx webpack-dev-server
css
预处理器less
,sass
(node-sass
),stylus
loader
less-loader
,sass-loader
,stylus-loader
js
文件
loader
(以less
为例)// webpack.config.js
...
module:{
rules:[
{
test:/\.less$/,
use:[
'style-loader',
{
loader:'css-loader',
options:{
modules:true
}
},
'postcss-loader',
'less-loader'
]
}
]
}
...
复制代码
css
文件
loader
(以less
为例)// webpack.config.js
...
module:{
rules:[
{
test:/\.css$/,
use:[
'style-loader',
{
loader:'css-loader',
options:{
modules:true
}
},
'postcss-loader',
'less-loader'
]
}
]
}
...
复制代码
css
样式上文中提到的样式,不难发现都是嵌套在
html
文件中的head
里的<style>
标签中的,可是在平常的开发中咱们通常都是单独在xxx.css
文件中写样式,而后经过<link>
引入,因此咱们接下来抽离css
样式吧json
mini-css-extract-plugin
(npm install mini-css-extract-plugin --save-dev
)webpack.config.js
,须要注意的是:
css-loader
处理后的字符串写到一个指定的文件中,想要使其以<link>
标签的形式引入,须要配合其自带的loader
使用,即下文的MiniCssExtractPlugin.loader
MiniCssExtractPlugin.loader
,开发模式使用style-loader
// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');// 引入插件
...
module.exports = {
...
plugins:[
new CleanWebpackPlugin(),
...htmlPlugins,
new MiniCssExtractPlugin({ // 注册插件
filename:'index.css'//指定抽离的文件名
})
...
],
module:{
rules:[
{
test:/\.css$/,
use:[
// 'style-loader'
MiniCssExtractPlugin.loader, // 配合插件自身loader使用
...
]
}
]
}
}
复制代码
npx webpack-dev-server
结果以下
css
样式文件npx webpack --mode production
,查看打包后的文件,你会发现咱们抽离的css
文件并无像js
,html
文件同样被压缩,这在咱们上线的时候确定是不容许的,因此咱们须要压缩它optimize-css-assets-webpack-plugin
(npm install optimize-css-assets-webpack-plugin --save-dev
)webpack.config.js
文件// webpack.config.js
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
...
module.exports = {
...
optimization:{ // 配置优化项
minimizer:[ // 手动指定压缩项
new OptimizeCssAssetsWebpackPlugin({})
]
}
mode:'production'
...
}
复制代码
npx webpack
查看结果,发现咱们提取的css
文件已经实现压缩
js
文件没有压缩,这是由于当咱们没有手动配置压缩项的时候webpack
内部本身实现了配置其自带的terser-webpack-plugin
插件进行压缩,而咱们配置后覆盖掉了它内部本身的配置,因此咱们须要手动将terser-webpack-plugin
配置到minimizer
中// webpack.config,js
const TerserJSPlugin = require('terser-webpack-plugin'); //webpack的内部插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
...
module.exports = {
...
optimization:{
minimizer:[
new TerserJSPlugin({}),// 手动加上压缩js
new OptimizeCssAssetsWebpackPlugin({})
]
}
mode:'production'
...
}
复制代码
npx webpack
查看结果image
文件夹并准备一张guYan.jpg
图片放入其中src/guYan.js
中引入guYan.jpg
// guYan.js
import url from '../image/guYan.jpg';
...
let img = new Image();
img.src = url;
documnt.body.appendChild(img)
复制代码
file-loader
(npm install file-loader --save-dev
)
webpack.config.js
//webpack.config.js
...
module.exports={
...
module:{
rules:[
...,
{
test:/\.(jpg|png|gif)$/,
use:'file-loader'
}
]
}
}
复制代码
npx webpack --mode production
查看打包后的文件,会发现生成一个拷贝的图片
Icon
之类的咱们更但愿它能够转化为base64
的格式压缩,因此这就须要咱们配置另外一个能够处理图片的loader
,url-loader
url-loader
(npm install url-loader --save-dev
)webpack.config.js
// webpack.config.js
...
module.exports={
...
module:{
rules:[
...,
{
test:/\.(jpg|png|gif)$/,
use:{
loader:'url-loader',
options:{
limit:100*1024 // 指定压缩文件的最大字节,超过最大限制会自动调用file-loader处理
}
}
}
]
}
}
复制代码
npx webpack --mode production
,查看打包后的文件目录并无生成拷贝的文件(尝试修改limit
参数的大下感觉一下变化)特殊注意:字体图标(
xxx.eot
,xxx.svg
,xxx.ttf
,xxx.woff
,xxx.woff2
)只能用file-loader
数组
若是你给的
limit
过大,也就是说会将大的图片也会转成base64
的格式会致使打包的时候会有一个性能的警告提示你文件过大,这样的时候你能够给一个performance:false
属性来使打包的时候忽略性能问题的提示,可是通常咱们上线确定考虑性能的问题的,也就是说咱们通常不会将limit
的值设置过大
js
文件中引用图片的例子,其实在开发中咱们也有可能直接在html
文件中引用图片,安装咱们以前的打包方式你会发现会出现找不到图片,由于路径变了,解决这个问题咱们须要配置一个html-withimg-loader
// webpack.config.js
...
{
test:/\.html$/,
use:'html-withimg-loader'
}
...
复制代码
url-loader
的options
增长publicPath
属性便可//webpack.config.js
...
options:{
limit:2*1024,
publicPath:'https://juejin.im/user/5b685661e51d4517df153771'
}
...
复制代码
同理
css``js
增长前缀一个套路
options
中增长outputPath
属性
css
filename
参数属性前面配置文件夹的名字
html
html-webpack-plugin
插件的filename
属性前面配置文件夹的名字
js
output
)里的filename
属性前面配置文件夹的名字
npx webpack --mode production
查看打包后的文件变化吧JS
相关JS
高级语法降级(经过babel
一系列包)guYan.js
文件以下// guYan.js
class Animal{
constructor(type){
this.type = type;
}
getType(){
return this.type
}
}
let animal = new Animal('哺乳类');
console.log(animal.type);
复制代码
babel-loader
,@babel/core
,@babel/preset-env
(npm install babel-loader @babel/core @babel/preset-env --save-dev
)webpack.config.js
// webpack.config.js
...
module.exports={
...,
module:{
rules:[
...,
{
test:/\.js$/,
use:{
loader:'babel-loader',
options:{
presets:['@babel/preset-env'],// 起指挥loader怎么干活的做用
}
},
exclude:/node_module/, // 忽略掉不想用loader处理的文件
include:path.resolve(__dirname,'./src') // 指定须要loader处理的文件
}
]
}
}
复制代码
npx webpack --mode development
,查看打包后的js
文件,咱们发现,他内部实现了一个_classCallCheck
,来做为转化咱们的class
类。// 删除后冗余的代码后的打包文件
(function(modules) {
var installedModules = {};
function __webpack_require__(moduleId) {
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true;
return module.exports;
}
return __webpack_require__(__webpack_require__.s = "./src/guYan.js");
})
({
"./src/guYan.js":
(function(module, exports) {
eval(`function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var Animal =function () { function Animal(type) { _classCallCheck(this, Animal); this.type = type; } _createClass(Animal, [{ key: \"getType\", value: function getType() { return this.type; } }]); return Animal; }(); var animal = new Animal('哺乳类'); console.log(animal.type);`);
})
});
复制代码
src/otherEs6.js
文件,在其中再用class
定义一个类导出,在src/guYan.js
中引入这个文件,再执行编译。你会发现他内部实现这个公共的_classCallCheck
代码,实现了两次,因此咱们须要提取这个公共的代码@babel/runtime
,@babel/plugin-transform-runtime
(npm install @babel/runtime @bebel/plugin-transform-runtime --save-dev
)webpack.config.js
文件// webapack.config.js
...
test:/\.js$/,
use:{
loader:'babel-loader',
options:{
presets:['@babel/preset-env'],
plugins:['@babel/plugin-transform-runtime']
}
}
...
复制代码
npx webpack --mode development
,查看打包后的js
文件,比较两次有什么不一样JS
的一些API
降级处理,如promise
(为了兼容IE
)@babel/preset-env
+core-js@3
core-js@3
(npm install core-js@3 --save
)webpack.config.js
// webpack.config.js
...
test:/\.js$/,
use:{
loader:'babel-loader',
options:{
preset:[
[
'@babel/preset-env',
{
useBuiltIns:'usage',// 只转化使用的API
corejs:{version:3}
}
]
]
}
}
...
复制代码
@babel/polyfill
@babel/plugin-transform-runtime
+@babel/runtime-corejs3
@babel/runtime-corejs3
(npm install @babel/runtime-corejs3
)webpack.config.js
// webapck.config.js
...
test:/\.js$/,
use:{
loader:'babel-loader',
options:{
presets:['@babel/preset-env'],
plugins:[
[
'@babel/plugin-transform-runtime',
{
"absoluteRuntime": false,
"corejs": {version:3},
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
}
...
复制代码
像装饰器和类的属性这些更高级的须要再去配插件去处理,这里我就不一一举例了(
@babel/plugin-proposal-decorators
,@babel/plugin-proposal-class-properties
)
ESlint
相关eslint
,eslint-loader
(npm install eslint eslint-loader --save-dev
)webpack.config.js
// webpack.config.js
...
test:/\.js$/,
use:'eslint-loader',
enforce:'pre' // 强制在编译以前执行
...
复制代码
eslint
npx eslint --init
webpack.config.js
// we
复制代码
ts-loader
tsconfig.json
plugin
和loader
的原理及实现create-react-app
实现一个简单的脚手架注: 参考文档