webpack4.0 CheatSheet

还在为webpack的配置而烦恼吗?这里有一份webpack从简易到高级版本的配置。还附赠配置地址,你想要吗?不,你不想。老老实实本身配置去吧。css

压箱底的笔记而已,你们看看乐乐就行了,这是笔者为了练习webpack而尝试了不一样的配置方式,参考了create-react-app的webpack配置。以及学习了如何本身写一个简易的proxy。html

Loaders全配置

Loader 做用
html-loader ---
html-webpack-plugin ---
style-loader ---
mini-css-extract-plugin 划重点,webpack4.0以后再也不使用extract-text-webpack-plugin
css-loader 一个将CSS变成JS的loader,笔者认为它的modules模块化是一个很实用的功能,大爱
sass-loader 一个SASS的处理器,先将scss编译成css,而后css再作进一步的处理
node-sass 编译scss依赖的包
postcss-loader 一款配合autoprefix,autoprefixer.github.io/ 自动给CSS加恼人的前缀
ts-loader 若是不用babel编译ts,则须要ts-loader
file-loader 导入文件,好比json,变成js的格式
url-loader 相似于file-loader,不过比file-loader智能,在文件过大的状况下能够只加载一个地址,而不用将文件载入
babel-loader 别说了,es6就须要他编译
webpack 你们都懂的,核心
webpack-cli 有了启动编译变得简单

babel配置

如今二进制能够直接编译jsnode

npx babel src --out-dir lib
复制代码

大声告诉我Babel是干什么的? ——由于JS语法一直在修订进步,而用户使用的浏览器更新频率不如JS语法更新的快,所以须要一个编译JS语法,使兼容支持不一样时期JS语法的浏览器。react

npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill
npm install --save-dev bable-loader
复制代码
@babel/polyfill vs @babel/preset-env
无视语法,直接require缺失api 救得了各类新颖写法(如箭头函数),救不了api
核心包core/js

@babel/preset-env

babel-preset-es2015
babel-preset-es2016
babel-preset-es2017
babel-preset-latest
A combination of the above ^
....
复制代码

噩梦般的配置,刚学习babel的时候,看到这些配置,隐约以为本身学不会了。可是如今babel-preset-env就能够搞定全部了!。webpack

@babel/plugin-proposal-class-properties babeljs.io/docs/en/bab…git

@babel/polyfill

这个包已经被babel给移除了,它仅仅是core-js的别名。es6

其余plugin

REACT全配置

多安装一个babel便可github

npm install --save-dev @babel/preset-react
npm install --save-dev react react-dom
复制代码

TS全配置

npm install --save-dev typescript
复制代码

ts能够用babel编译web

npm install --save-dev @babel/preset-typescript
复制代码

ts也能够用ts-loader编译,须要配置tsconfig.jstypescript

npm install --save-dev ts-loader
复制代码

有关CSS加载的一些看法

CSS loader能够很简单,也能够至关复杂,通常的需求有如下几点:

  • 有效CSS,直接inline的加载
  • 但愿能够单独生成一个文件,而后url加载
  • 但愿能够压缩一下
  • 想要自动加前缀的功能
  • 使用SCSS等,高级CSS处理器

参考create-react-app的配置文件,写的一个一本知足的css loader大餐:

// mini-css-extract-plugin,有了他能够代替style-loader,不只压缩了文件,还能够帮助咱们将CSS从js中剥离出来
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

//loaders的主体,按照loader的顺序应该是先是预处理,好比Scss,而后是加前缀prefix,到此为止你们仍是CSS的样子,等到了css-loader,css就变成了js,最后style-loader或者minicss将css模块再变成js生成文件或者内联。
const getStyleLoaders = (cssOptions, preProcessor,env) => {
    const loaders = [
        // 此处是最后一步,将CSS提取出或者内联
        {loader:env==="development"?require.resolve('style-loader') : MiniCssExtractPlugin.loader},
        // 此处将CSS变成标准的JS模块,若是有css模块化的需求,是在此处理
        {
            loader: require.resolve('css-loader'),
            options: cssOptions,
        },
        // 此处将CSS预处理处,通常是给css加上恼人的前缀,这里能够设置浏览器的版本,你须要哪些浏览器的支持
        {
            loader: require.resolve('postcss-loader'),
            options: {
                ident: 'postcss',
                plugins: () => [
                require('postcss-flexbugs-fixes'),
                require('postcss-preset-env')({
                    autoprefixer: {
                    flexbox: 'no-2009',
                    },
                    stage: 3,
                }),
                ],
            },
        },
    ];
    // 此处若是有sass等预处理的需求,须要在此配置
    if (preProcessor) {
        loaders.push(require.resolve(preProcessor));
    }
    return loaders;
};
// style files regexes,这里是为了不重复配置css的各类类型
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;

// 导出给webpack配置使用
module.exports={getStyleLoaders,cssRegex,cssModuleRegex,sassRegex,sassModuleRegex}

复制代码

HMR热更新配置——减小手动编译刷新页面

webpack自带的服务器

npm install --save-dev webpack-dev-server
复制代码
devServer: {
    contentBase: path.join(__dirname, 'dist1'),
    compress: true,
    hot: true,
    port: 9000
},
entry: [
    require.resolve('webpack-dev-server/client') + '?/',
    require.resolve('webpack/hot/dev-server'),
    path.resolve(__dirname,'src/index')
],
plugins: [
    new webpack.HotModuleReplacementPlugin()
]
复制代码

webpack自带的服务器的跨域问题

devServer: {
    proxy: {
      '/slider': {
          target: 'http://www.cherryvenus.com/',
          secure: false,
          changeOrigin: true//important 能解决大多数404没法访问的问题
      }
    }
},
复制代码

自定义服务器

npm install --save-dev express webpack-hot-middleware webpack-dev-middleware
复制代码

client,webpack,此处不能忘,其实就是一个连接websocket的一个相似于插件的程序,记得production的时候将此处移除,否则bundle会很大呢。

entry: [
    require.resolve('webpack-hot-middleware/client') + '?path=/__what&timeout=2000&overlay=false',
    path.resolve(__dirname,'src/index')
],
plugins: [
    new webpack.HotModuleReplacementPlugin()
]
复制代码

server,此处的path配置须要和客户端的path一致。

app.use(require("webpack-hot-middleware")(compiler,{
  log: false,
  path: "/__what",
  heartbeat: 2000
}));
复制代码

entry入口的client添加模块,不accept,程序没法自动刷新。

if(module.hot){
  module.hot.accept()
}
复制代码

http-proxy-middleware——解决开发中的跨域问题

npm install --save-dev http-proxy-middleware
复制代码
var proxyMiddleWare = require("http-proxy-middleware");
var proxyPath = "http://www.cherryvenus.com/";
var proxyOption ={target:proxyPath,changeOrigoin:true};
app.use(proxyMiddleWare("/slider",proxyOption))
复制代码

压箱底的proxy研究

基于第三方插件的proxy配置开发

不管是http-proxy-middleware仍是webpack-dev-server的proxy都是基于http-proxy-middleware,而http-proxy-middleware是基于node-http-proxy。

参考网址:

github.com/nodejitsu/n…

github.com/chimurai/ht…

var proxyPath = "http://www.cherryvenus.com/";
var proxyOption ={target:proxyPath,changeOrigin:true,selfHandleResponse : true,ignorePath:true};
const proxy = httpProxy.createProxyServer({})

//若是selfHandleResponse为true就是能够本身修改得到的内容,若是不须要修改就是获取转发就无需设置了。


// 若是想要自定义获取到的内容,则能够经过触发这个事件`proxyRes`来截获内容并修改

proxy.on('proxyRes', function (proxyRes, req, res) {
    var body = new Buffer('');
    
    // 这里笔者复制了header信息,省去了一波配置
    
    for(let i in proxyRes.headers){
        res.setHeader(i, proxyRes.headers[i]);
    }
    proxyRes.on('data', function (data) {
        body = Buffer.concat([body, data]);
    });
    proxyRes.on('end', function () {
        body = Buffer.from(body);
        res.write(body);
        res.end()
        res.rs()
    });
    proxyRes.on('error', function (err, req, res) {
        res.writeHead(500, {
          'Content-Type': 'text/plain'
        });
        res.end('Something went wrong. And we are reporting a custom error message.');
    });
})

// 此处仅仅是配置了一个连接而已,真正触发代理,是下方的proxy.web(req, res, proxyOption);

app.use("/slider/",(req, res, next)=>{
    // 当地址地位到
    let p= new Promise((rs,rj)=>{
       // 先关闭以前的代理,若是有
        proxy.close();
        res.rs=rs
        // 请求一个新的连接
        proxy.web(req, res, proxyOption);
    })
    p.then(()=>{
        next()
    })
    return p
})
复制代码

手动写一个

看到上面的解决方法,忽然灵光一闪,那么我是否是能够本身写一个呢?这个流程并不复杂,其实代理就是抓取网页而后转发的过程,既然如此,写一个简易的proxy并非什么难事。

const http=require("http")
// 建立服务器
http.createServer({ host : 'localhost'}, (req, res) => {
    // 打开一个请求
    http.get('http://www.baidu.com', (res1) => {
        // 抓取内容
        var body = new Buffer('');
        res1.on('data', (chunk) => { 
            body = Buffer.concat([body, chunk]);
         });
        res1.on('end', () => {
            body = Buffer.from(body);
            // 复制头部信息
            for(let i in res1.headers){
                res.setHeader(i, res1.headers[i]);
            }
            // 返回内容,代理成功
            res.write(body);
            res.end();
        });
    }) 
}).listen(8000);
复制代码

代理就是如此简单,固然这只是一个原理,一种实现方法,就是抓取网页再打印到页面上。

而后笔者在查阅node中http发现一个惊为天人的写法(也许是笔者见识浅薄),一个利用了Http的method为CONNECT的方法,将当前连接处于连接状态,也就是不会断,而后用 net.connect这个方法,连接到了请求连接,创建了互读互写的管道。

const http=require("http")
const net=require("net")
const url=require("url")
const proxy = http.createServer((req, res) => {
    const options = {
        port: 1337,
        host: 'localhost',
        method: 'CONNECT',//会触发"connect" https://nodejs.org/api/http.html#http_event_connect
        path: 'www.baidu.com:80'
    };
    const req1 = http.request(options);
    req1.end();
    req1.on('connect', (res1, socket, head) => {
        let context=new Buffer("")
        // make a request over an HTTP tunnel
        socket.write('GET / HTTP/1.1\r\n' +
                    'Host: www.baidu.com:80\r\n' +
                    'Connection: close\r\n' +
                    '\r\n');
        socket.on('data', (chunk) => {
            context = Buffer.concat([context, chunk]);
        });
        socket.on('end', () => {
            for(let i in req.headers){
                res.setHeader(i, req.headers[i]);
            }
            context=context.toString().split("\n\r\n")
            context.shift()
            res.end(context.join(""))
        });
    });
}).listen(1337);
proxy.on('connect', (req, cltSocket, head) => {
    const srvUrl = url.parse(`http://${req.url}`);
    const srvSocket = net.connect(srvUrl.port, srvUrl.hostname)
    cltSocket.write('HTTP/1.1 200 Connection Established\r\n' +
    'Proxy-agent: Node.js-Proxy\r\n' +
    '\r\n');
    srvSocket.write(head);
    //管道创建了互读互写
    srvSocket.pipe(cltSocket).pipe(srvSocket)
});
复制代码

这个方法的简易原理是这样的,当你访问http server的时候,http server调起了一个访问自身连接,而且设置method为CONNECT,也就是不间断的意思,并建立了一个clientSocket,而后此http server也监听了connect的事件,当有method为CONNECT的连接的时候,会触发http server的connect。此时连接打通以后,http server就会建立一个net的连接,去访问须要代理的网站,返回一个serverSocket,而后serverSocket负责读取内容,而后写入clientSocket,clientSocket再告诉serverSocket写入成功,直至代理成功。

webpack的拆包实践(三种方法)

optimization.splitChunks

真拆包,参考连接

module.exports = {
  //...
  optimization: {
    splitChunks: {
      // ...
      minChunks: 2,// 这个属性配置了当前模块在不一样的模块中出现的次数,若是出现了引用两次的状况,则复用打包出来,这个是真拆包,拆的本身包。
      // ...
    }
  }
};
复制代码

externals剔除没必要要的依赖包

只要注意root的问题,root在web下至关于window,所以是window.React,prop-type更不用说了。

externals:{
    react:{ 
       root: 'React',
       amd: 'react',
       commonjs: 'react',
       commonjs2: 'react' 
    },
    "prop-types":{ 
      root: 'PropTypes',
      amd: 'prop-types',
      commonjs: 'prop-types',
      commonjs2: 'prop-types' 
    }
}
复制代码

dll和external的区别

exteranl是剔除别人的包。

dll是建立本身的工具库。

const library = '[name]_lib'
module.exports = {
  mode:"production",
  entry: {
    vendors: ['react', 'react-dom']
  },
  output: {
    filename: '[name].dll.js',
    path: path.join(__dirname,"dist/vendor"),
    //libraryTarget: "umd",
    library
  },
  plugins: [
    new webpack.DefinePlugin({ 
      'process.env': { //用于打包react中的东西
        NODE_ENV: JSON.stringify("production")
      } 
    }),
    new webpack.DllPlugin({
      path: path.join(__dirname, 'dist/[name]-manifest.json'),
      // This must match the output.library option above
      name: library
    }),
  ]
}
复制代码

production中使用动态连接库,此处不要配置external。

new webpack.DllReferencePlugin({
    context: __dirname,
    manifest: path.join(__dirname, 'dist/vendors-manifest.json'),
})
复制代码

个人博客即将同步至腾讯云+社区,邀请你们一同入驻:cloud.tencent.com/developer/s…

相关文章
相关标签/搜索