美文网首页
使用webpack4从0开始打包一个antd项目

使用webpack4从0开始打包一个antd项目

作者: 名侦探柯妍 | 来源:发表于2020-06-13 22:24 被阅读0次

    初始化项目

    $ npm install -g cnpm --registry=https://registry.npm.taobao.org // 安装cnpm

    $ mkdir project // 根目录下创建一个名为project的文件夹

    $ cd project && cnpm init // 进入project文件夹 并初始化该项目 终端会提示一些配置,一路键入enter最后yes即可。 初始化完成会生成一个pakage.json文件

    安装webpack (4.x)

    $ cnpm i --D webpack webpack-dev-server webpack-cli // webpack4.x 必须安装webpack-cli。

    根目录下新建文件src/index.js

    image

    $ npm run build // 此时我们可以执行npm run build 代替npx webpack。 构建成功后根目录下自动生成 dist/main.js。

    为什么我们没有对webpack进行任何配置,却可以成功打包?那是因为在没有配置入口文件的情况下, webpack 4.x会自动查找src/index.js作为入口文件进行打包。

    webpack配置

    这里我们需要了解webpack4个核心概念——入口(entry)、输出(output)、loader、插件(plugins)。

    1.对js文件进行打包

    根目录下创建webpack.config.js

    const path = require("path");
    
    module.exports = {
        // 指定构建环境  
        mode:"development",
        // 入口
        entry: {
          main: path.resolve(__dirname, './src/index.js')  // 配置入口文件为src/index.js
        },
        // 出口
        output: {
            path : path.resolve(__dirname, "./dist"), // 出口文件放到dist文件夹下
            filename: "[name].js",  // 这里的[name] 取的是 entry里的main,如果entry里配置的为app: path.resolve(__dirname, './src/index.js') 则打包出的文件为dist/app.js
            publicPath: "/" // 打包后的资源的访问路径前缀
        },
        // 模块
        module:{
        },
        // 插件
        plugins:[
        ],
        // 开发环境本地启动的服务配置
        devServer: {
        }
    }
    

    $ cnpm run build
    我们可以看到根目录下打包出了dist/main.js。
    这时候如果我们将webpack.config.js中入口配置改为

    entry: {
      app: path.resolve(__dirname, './src/index.js')
    }
    

    $ cnpm run build


    截屏2020-06-13 下午9.28.13.png

    重新构建我们发下dist目录下多出了一个app.js文件,上一次打包的main.js还在,如果我们希望每次打包之前先清除掉上一次的打包文件,则需要用到clean-webpack-plugin插件。

    $ cnpm i -D clean-webpack-plugin // vesion: 6.13.4 (6.x版本与低版本引入方式不一样)
    安装后我们在webpack.config.js里进行配置 。
    首先引入clean-webpack-plugin


    [图片上传中...(截屏2020-06-13 下午9.33.58.png-d29026-1592055241921-0)]

    再在plugins里实例化


    截屏2020-06-13 下午9.34.40.png
    然后我们将 entry: { app: {...}} 中的app改回main

    $ cnpm run build
    重新构建,我们可以看到上一次打包出的dist/app.js已经删掉了。

    2.增加html文件

    首先我们在dist目录下手动增加一个index.html文件,引入main.js

    <!doctype html>
    <html>
    
    <head>
      <meta charset="utf-8">
      <title>react-music</title>
      <meta name="viewport" content="width=device-width,initial-scale=1">
    </head>
    
    <body>
      <div id="root"></div>
      <script src="main.js"></script>
    </body>
    </html>
    

    在chrome中打开该html文件,我们可以看到控制台中成功打印出hello world


    截屏2020-06-13 下午9.45.25.png

    那么问题来了,难道我们每次打包需要手动在dist目录下添加一个html文件吗? 显然不可能,这时候我们需要用到html-webpack-plugin插件自动添加html文件。

    我们先将dist/index.html文件移动到src下,去掉<script>标签。
    $ cnpm i -D html-webpack-plugin
    安装好后一样先引入插件 具体插件配置参考官网

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
        template: 'src/index.html', // 自动生成的html文件以src/index.html为模板生成
        inject: true, // true:默认值,script标签位于html文件的 body 底部
        hash: true, // 在打包的资源插入html会加上hash
        minify: {
          removeComments: true,               //去注释
          collapseWhitespace: true,           //压缩空格
          removeAttributeQuotes: true         //去除属性 标签的 引号  例如 <p id="test" /> 输出 <p id=test/>
        }
      }),
    ]
    

    $ cnpm run build
    此时我们看到dist目录下自动生成了一个index.html文件,里面包含了<div id="root"></div>,
    这是因为我们在模板文件里写过。之前我们已经安装过了webpack-dev-server,并在package.json脚本里配置了"dev": "webpack-dev-server" 此时我们可以运行

    $ cnpm run dev
    终端提示自动运行在localhost:8080, chrome上打开8080端口,查看控制台,我们可以看到打印出了hello world。

    3.丰富webpack-dev-server配置
      // 开发环境本地启动的服务配置
      devServer: {
        historyApiFallback: true, // 当找不到路径的时候,默认加载index.html文件
        hot: true,
        contentBase: false, // 告诉服务器从哪里提供内容。只有在你想要提供静态文件时才需要
        compress: true, // 一切服务都启用gzip 压缩:
        port: "8989", // 指定端口号
        publicPath: "/", // 访问资源加前缀
        proxy: {
            // 接口请求代理
        },
      }
    

    $cnpm run dev


    截屏2020-06-14 上午9.48.49.png

    这时我们可以看到该项目已经运行在我们制定的端口8989上了。

    打包css/less 、图片、字体等。

    $ cnpm i -D style-loader css-loader less-loader file-loader url-loader
    我们在src目录下新建一个images文件夹,然后拖入一张图片,这里图片名用了smokinggirl.jpeg。 再在src目录下新建一个index.less文件。
    然后修改src/index.js src/index.less

    import advatar from './images/smokinggirl.jpeg'; // 引入图片地址
    import './index.less'; // 引入index.less
    
    const root = document.getElementById('root');
    const img = new Image();
    img.src = advatar;
    img.classList.add('advatar'); // 为图片增加类
    root.appendChild(img);
    

    src/index.less

    @img-width: 200px;
    
    .advatar {
      width: @img-width;
    }
    

    webpack.config.js中module部分增加配置

        module:{
          rules: [{
            test: /\.(jpg|jpeg|png|gif)$/,   // 正则匹配文件后缀
            use: {
              loader: 'file-loader',
              options: {
                name: 'images/[name].[ext]'
              }
            }
          }, {
            test: /\.css$/,
            use: ['style-loader', 'css-loader'], 
          }, {
            test: /\.less$/,
            loader: 'style-loader!css-loader!less-loader'  // 等同于use: ['style-loader', 'css-loader', 'less-loader']
          }]
        },
    

    $ cnpm run dev
    http://localhost:8989 可以看到有一个宽200px的图片显示出来。
    这样打包出来的css 样式会作用到全局,如果页面多可能会相互影响,这时候我们可以启用css模块,配置稍作修改

        // 模块
        module:{
          rules: [{
            test: /\.(jpg|jpeg|png|gif)$/,
            use: {
              loader: 'file-loader',
              options: {
                name: 'images/[name].[ext]'
              }
            }
          }, {
            test: /\.css$/,
            exclude:/node_modules/,
            use: [{
              loader: 'style-loader',
            }, {
              loader: 'css-loader',
              options: {
                modules: true
              }
            }],
          }, {
            test: /\.less$/,
            exclude:/node_modules/,
            use: [{
              loader: 'style-loader',
            }, {
              loader: 'css-loader',
              options: {
                modules: true,
              }
            }, {
              loader: 'less-loader',
            }]
          }]
        },
    

    src/index.js

    import advatar from './images/smokinggirl.jpeg'; // 引入图片地址
    import styles from './index.less';  // 引入方式改变
    
    const root = document.getElementById('root');
    const img = new Image();
    img.src = advatar;
    img.classList.add(styles.advatar); // 从styles对象里获取类名
    root.appendChild(img);
    

    $cnpm run dev
    这时候我们可以看到图片成功显示出来,但仔细观察图片类名,是一串自动生成的字符,与我们自己定义的类名没有半点关系,那么如何显示出我们定义的类名呢?


    截屏2020-06-14 上午11.18.35.png
       {
            test: /\.less$/,
            use: [{
              loader: 'style-loader',
            }, {
              loader: 'css-loader',
              options: {
                modules: {
                  localIdentName: '[path][name]__[local]--[hash:base64:5]',  // 这里可以自定义以什么样的形式打包出类名。
                }
              }
            }, {
              loader: 'less-loader',
            }]
       }
    

    $ cnpm run dev 这时候可以看到打包出的类名符合我们定义的形式。


    截屏2020-06-14 上午11.24.37.png

    但仔细观察,我们发现body还有默认样式,我们可以在index.html引入一个公共样式文件,去除默认样式。
    src目录下 新建common/reset-style.css, 写个简单的去默认样式的文件

    body {
      margin: 0;
      padding: 0;
    }
    ul {
      list-style: none;
      margin: 0;
      padding: 0;
    }
    

    然后在html模板文件 src/index.html中引入,

    <head>
      <meta charset="utf-8">
      <title>project</title>
      <meta name="viewport" content="width=device-width,initial-scale=1">
      <link rel="stylesheet" href="../common/reset-style.css">
    </head>
    

    这时候我们运行,发现报错找不到该文件。这是因为我们在index.html里引入该文件,但webpack没有打包编译。这时候我们需要用到插件copy-webpack-plugin
    $ cnpm i -D copy-webpack-plugin

    const CopyWebpackPlugin = require('copy-webpack-plugin');
    
        plugins:[
          new CleanWebpackPlugin(),
          new HtmlWebpackPlugin({
            template: 'src/index.html',
            inject: true, // true:默认值,script标签位于html文件的 body 底部
            hash: true, // 在打包的资源插入html会加上hash
            minify: {
              removeComments: true,               //去注释
              collapseWhitespace: true,           //压缩空格
              removeAttributeQuotes: true         //去除属性 标签的 引号  例如 <p id="test" /> 输出 <p id=test/>
            }
          }),
          new CopyWebpackPlugin({
            patterns: [
              {
                from: path.resolve(__dirname, './common'),  // 从哪个目录copy
                to: "common", // copy到那个目录
                globOptions: {
                  ignore: ['.*'],
                }
              }
            ],
          }),
        ],
    

    重新运行,我们可以看到原先的默认样式body的边距已经没有了。

    引入React

    $ npm i -S react react-dom
    安装完react、react-dom 还不够,我们需要babel做翻译,才能让浏览器读懂。

    $ cnpm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/plugin-transform-runtime @babel/runtime-corejs2

    • @babel/preset-react 翻译react语法
    • @babel/preset-env es6、es7转换成es5
    • @babel/plugin-transform-runtime 对@babel/preset-env无法转换的es6、es7的新特性进行转换。
    • @babel/runtime只能处理语法关键字,而@babel/runtime-corejs2还能处理新的全局变量(例如,Promise)、新的原生方法(例如,String.padStart );因此我们使用@babel/runtime-corejs2 就无须再使用 @babel/runtime了。

    安装好后根目录新建.babelrc文件。

    {
      "presets": [
        ["@babel/preset-env", {
          "modules": false,
          "targets": {
            "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
          }
        }],
        "@babel/preset-react"
      ],
      "plugins": [
        ["@babel/plugin-transform-runtime",{
          "corejs": 2, // polyfill 需要使用@babel/runtime-corejs2
          "useBuildIns":"usage", //按需引入,即使用什么新特性打包什么新特性, 可以减小打包的体积
        }]
      ]
    }
    

    webpack.config.js 的module里增加babel-loader的配置

     {
          test: /\.jsx?$/,//一个匹配loaders所处理的文件的拓展名的正则表达式,这里用来匹配js和jsx文件(必须)
          exclude: /node_modules/,//屏蔽不需要处理的文件(文件夹)(可选)
          loader: 'babel-loader',//loader的名称(必须) loader接收字符串, use接收对象或数组
      }
    

    配置好我们来写react文件
    新建src/page/content/index.js, 将src下的index.less拖入content目录下。

    import React, { Component } from 'react';
    import advatar from '../../images/smokinggirl.jpeg'; // 引入图片地址
    import styles from './index.less';
    
    class Content extends Component {
      render() {
        return (
          <div>
            <h2>这是一张图片</h2>
            <img src={advatar} className={styles.advatar} />
          </div>
        )
      }
    }
    export default Content
    

    接下来改造src/index.js 入口文件

    import React, { PureComponent } from 'react';
    import ReactDOM from 'react-dom';
    import Content from './page/content';
    
    class App extends PureComponent {
      render() {
        return (
          <div>
            <Content />
          </div>
        )
      }
    }
    
    ReactDOM.render(<App />, document.getElementById('root'));
    

    然后运行 我们可以看到页面正常显示。到这里我们已经可以正确打包并运行react项目了。

    引入antd

    $ cnpm i -S antd
    根据antd官网描述,我们还需要在入口文件引入一个样式文件。
    src/index.js

    import 'antd/dist/antd.css';
    

    到这里我们可以发现一个问题,我们对css文件打包的时候启用了模块,很明显这样直接引用是不行的。 antd的样式是从node_modules里引用的,那么我们的思路就是打包css文件时去除掉node_modules里面的文件再启用模块,node_modules里面的文件不启用模块。
    接下来我们对webpack.config.json进行改造。

        // 模块
        module:{
          rules: [{
            test: /\.(jpg|jpeg|png|gif)$/,
            use: {
              loader: 'file-loader',
              options: {
                name: 'images/[name].[ext]'
              }
            }
          }, {
            test: /\.css$/,
            exclude:/node_modules/,
            use: [{
              loader: 'style-loader',
            }, {
              loader: 'css-loader',
              options: {
                modules: {
                  localIdentName: '[path][name]__[local]--[hash:base64:5]',
                }
              }
            }],
          }, {
            test: /\.css$/,
            include: /node_modules/,
            use: [{
              loader: 'style-loader',
            }, {
              loader: 'css-loader',
              options: {
                modules:  false,
              }
            }]
          }, {
            test: /\.less$/,
            exclude:/node_modules/,
            use: [{
              loader: 'style-loader',
            }, {
              loader: 'css-loader',
              options: {
                modules: {
                  localIdentName: '[path][name]__[local]--[hash:base64:5]',
                }
              }
            }, {
              loader: 'less-loader',
            }]
          }, {
            test: /\.jsx?$/,//一个匹配loaders所处理的文件的拓展名的正则表达式,这里用来匹配js和jsx文件(必须)
            exclude: /node_modules/,//屏蔽不需要处理的文件(文件夹)(可选)
            loader: 'babel-loader',//loader的名称(必须) loader接收字符串, use接收对象或数组
          }]
        },
    

    只有这样还不够,我们需要用到babel-plugin-import插件实现按需加载,antd样式应用到全局需要用到此插件。
    $cnpm i -D babel-plugin-import
    .babelrc

      "plugins": [
        ["@babel/plugin-transform-runtime",{
          "corejs": 2, // polyfill 需要使用@babel/runtime-corejs2
          "useBuildIns":"usage", //按需引入,即使用什么新特性打包什么新特性, 可以减小打包的体积
        }],
        ["import", { // babel-plugin-import 按需加载插件 项目组件css使用modules方式打包,antd样式全局引用 需要用到此插件。
          "libraryName":"antd",
          "libraryDirectory":"es",
          "style": "css"
        }]
      ]
    

    ok现在我们来改造一下src/page/content/index.js文件,引入一个antd组件。

    import React, { Component } from 'react';
    import { Radio } from 'antd';
    import advatar from '../../images/smokinggirl.jpeg'; // 引入图片地址
    import styles from './index.less';
    
    class Content extends Component {
      constructor(props) {
        super(props);
        this.state = {
          type: 'img',
        }
      }
    
      handleChange = e => {
        const val = e.target.value;
        this.setState({
          type: val,
        });
      }
    
      render() {
        const { type } = this.state;
        return (
          <div>
            <Radio.Group value={type} onChange={this.handleChange}>
              <Radio value="img">图片</Radio>
              <Radio value="text">文本</Radio>
            </Radio.Group>
            <h2>{type === 'img' ? '这是一张图片' : '这是一段文案'}</h2>
            {
              type === 'img'
              ? 
              <img src={advatar} className={styles.advatar} />
              :
              <p>这是一大段文案</p>
            }
          </div>
        )
      }
    }
    export default Content
    

    现在我们来运行,发现报了个错


    截屏2020-06-14 下午3.35.50.png

    提示我们需要安装一个@babel/plugin-proposal-class-properties插件。
    $ cnpm i -D @babel/plugin-proposal-class-properties
    然后我们需要在babel的配置文件中加入它

      "plugins": [
        ["@babel/plugin-transform-runtime",{
          "corejs": 2, // polyfill 需要使用@babel/runtime-corejs2
          "useBuildIns":"usage", //按需引入,即使用什么新特性打包什么新特性, 可以减小打包的体积
        }],
        ["import", { // babel-plugin-import 按需加载插件 项目组件css使用modules方式打包,antd样式全局引用 需要用到此插件。
          "libraryName":"antd",
          "libraryDirectory":"es",
          "style": "css"
        }],
        ["@babel/plugin-proposal-class-properties"]
      ]
    

    现在我们重新运行 $ cnpm run dev 一个简单的包含antd组件的页面就完成了。


    截屏2020-06-14 下午3.38.46.png
    截屏2020-06-14 下午3.39.14.png

    相关文章

      网友评论

          本文标题:使用webpack4从0开始打包一个antd项目

          本文链接:https://www.haomeiwen.com/subject/oqftxktx.html