得自在禅 發表於 2019-7-13 13:58:00

从零开始搭建一个react项目

<h1 class="postTitle">从零开始搭建一个react项目</h1>
<div class="clear">&nbsp;</div>
<div class="postBody">
<div id="cnblogs_post_body" class="blogpost-body">
<blockquote>
<p><em>欢迎加入qq群:36952712<br></em></p>
<p><em>如果只是想试试 React,那么建议使用 create-react-app来创建一个react项目。</em>快速开始&nbsp;<br><strong>因为 create-react-app 和 vue-cli 不一样,create-react-app将webpack的相关配置直接封装好了,所以自定制化程度不高,所以考虑手动构建一个 React项目</strong></p>
<p>代码下载</p>

</blockquote>
<h1>准备工作</h1>
<ol class="wiz-list-level1">
<li>安装node环境。</li>
<li>配置cnpm(看个人需求)。</li>
<li>准备一个空的文件夹react-demo。</li>

</ol>
<h1>初始化工程</h1>
<p>从这里开始新建一个react工程</p>
<h3 id="1.初始化工程目录">1. 初始化工程目录</h3>
<div data-mode="bash">
<div class="cnblogs_code">
<pre>1 cd react-demo
2 npm init</pre>
</div>
<p>&nbsp;</p>
</div>
<p>一路回车,我们将得到一个最简单的npm目录,会包含一个package.json。</p>
<div data-mode="json">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>// package.json
{
"name": "react-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" &amp;&amp; exit 1"
},
"author": "",
"license": "ISC"
}</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<p>然后在react-demo目录下新建一个程序的主目录" src " 目录。<code>mddir src</code></p>
<h3 id="2.配置工程">2. 配置工程</h3>
<h4 id="1.webpack初体验">1. webpack初体验</h4>
<p>首先安装webpack和webpack-cli。&nbsp;<em>webpack4.X</em></p>
<div data-mode="bash">
<div class="cnblogs_code">
<pre>npm install --save-dev webpack webpack-cli -g</pre>
</div>
<p>&nbsp;</p>
</div>
<blockquote>
<p>Q:--save-dev 和 --save的区别&nbsp;<br>A:--save-dev是你开发时候依赖的东西,--save是你发布之后依赖的东西。区别&nbsp;<br><strong>TIPS: -g表示全局安装,--save-dev 可以简写为 -D ,--save 可以简写为 -S ,npm install 可以简写为 npm i</strong></p>

</blockquote>
<h5 id="新建并配置webpack">新建并配置webpack</h5>
<p>新建webpack.config.js 文件。首先要理解webpack的几个基础概念<em>入口(entry)</em>、<em>出口(output)</em>、<em>载入器(loader)</em>、<em>插件(plugins)</em>、<em>模式(mode)</em>&nbsp;。webpack中文文档。代码如下:</p>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre> 1 // __dirname是node.js中的一个全局变量,它指向当前执行脚本所在的目录
2 // path是node.js中提供的处理文件路径的小工具。 (http://www.runoob.com/nodejs/nodejs-path-module.html)
3 const path = require('path');
4 module.exports = {
5   // 项目入口,webpack从此处开始构建
6   entry: {
7         main: path.join(__dirname, 'src/index.js'), // 指定入口,可以指定多个。参考webpack文档
8   },
9   output: {
10         path: path.join(__dirname, "dist"), // bundle生成(emit)到哪里
11         filename: "bundle.js", // bundle生成文件的名称
12   },
13 }</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<ul>
<li>这样就完成了最简单的webpack配置文件。相应的我们需要在src目录下新建一个index.js 文件。</li>
<li>接下来在命令行输入&nbsp;<code>webpack --config ./webpack.config.js</code>就可以输出dist文件夹。</li>
<li>为了方便起见,通常我们会在package.json里配置脚本命令。在scripts标签下,添加一句<code>"build": "webpack --config ./webpack.config.js"</code>这样,我们就可以通过<code>npm run build</code>完成webpack打包。</li>
</ul>
<h5 id="配置开发应用服务器">配置开发应用服务器</h5>
<p>正常情况下,我们需要以应用服务器打开我们的网页,webpack-dev-server提供了一个简单的web服务器,并且能够实时重新加载。指南&nbsp;首先需要安装webpack-dev-server&nbsp;<code>npm install -D webpack-dev-server</code>。&nbsp;<br>接下来,修改配置文件,告诉开发服务器,在哪里寻找文件:</p>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>1 // webpack.config.js
2 module.exports = {
3   devServer: {
4         contentBase: './dist'
5   }
6 }</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<p>这段配置告诉webpack-dev-server,在默认host和port下建立服务,并将contentBase目录下的目录,作为可访问文件。&nbsp;<br>接下来让我们的服务器跑起来,在package.json配置如下的命令脚本:</p>
<div data-mode="js">
<div class="cnblogs_code">
<pre>1 "scripts" : {
2   "start": "webpack-dev-server --mode development --open",
3   "build: "webpack --mode production --config ./webpack.config.js"
4 }</pre>
</div>
<p>&nbsp;</p>
</div>
<p>其中mode是上文中提到模式概念,webpack会有相应的内置优化。</p>
<h5 id="Babel&amp;React">Babel &amp; React</h5>
<p>ES6已经极为流行了,不过目前仍有浏览器不兼容。同时react的jsx语法,也需要babel来将其转化为能兼容的js代码。</p>
<div data-mode="bash">
<div class="cnblogs_code">
<pre>1 npm install react react-dom react-router-dom -S
2 npm install babel-core babel-loader babel-preset-env babel-preset-react -D</pre>
</div>
<p>&nbsp;</p>
</div>
<p>安装完之后,我们需要在webpack中配置使其生效。在webpack.config.js 的module中添加rules规则,如下:</p>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>module.exports = {
    // ...省略
    module: {
      rules: [

{
    test: /\.(js|jsx)$/,
    exclude: /node_modules/,
    enforce: 'pre',
    use: [{
      loader: 'babel-loader',
    }, {
      loader: 'eslint-loader', // 指定启用eslint-loader
      options: {
            formatter: require('eslint-friendly-formatter'),
            emitWarning: false
      }
    }]
},
    ]
}</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<p>使用babel-loader我们需要配置相应的规则,我们可以这样配置:&nbsp;<br><img src="https://images2018.cnblogs.com/blog/1111680/201806/1111680-20180620182241506-1288579415.png" alt="">&nbsp;<br>同时考虑到,后期可能会别的规则需求,例如使用antDesign的按需引入,我们将babel的配置提出来,在根目录下新建文件&nbsp;<code>.babelrc</code>,并书写以下代码。</p>
<div data-mode="json">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>{
"presets": ["env","react"],
// antd按需引入
// "plugins": ["react-hot-loader/babel", ["import", { "libraryName": "antd", "libraryDirectory": "es","style": "css" }], "transform-runtime"]
}</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<p>到这里,我们就完成babel的相关配置,并且安装了react相关依赖,可以书写jsx语法了。</p>
<h5 id="关于样式">关于样式</h5>
<p>前置安装less-loader/scss-loader、style-loader、css-loader、postcss-loader。</p>
<ul>
<li>less-loader是将less编译成css的loader</li>
<li>postcss是将css加上浏览器Hack的loader</li>
<li>css-loader用于在js中import、require等方法引入css</li>
<li>style-loader用于将css最终写入html文件。</li>
</ul>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>test: /\.(css|less)$/,
exclude: /node_modules/,
include: /src/,
use: [
    {loader: "style-loader"},
    {
      loader: 'css-loader',
      options: {
            minimize: process.env.NODE_ENV === 'production',
            importLoaders: 2,
            localIdentName: '--',
            modules:true
      }
    }, {
      loader: 'postcss-loader',
      options: {         // 如果没有options这个选项将会报错 No PostCSS Config found
            plugins: (loader) =&gt; [
                require('autoprefixer')(), //CSS浏览器兼容
            ]
      }
    },{
      loader: 'less-loader',
      options: {
            javascriptEnabled: true,
      }
    }],</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<p>其中test对应的可以匹配的正则文件,include是需要编译的目录,exclude是跳过的目录,use里面可以书写跟loader相关的配置。</p>
<blockquote>
<p><strong>注意loader相关的顺序,特别是使用ExtractTextWebpackPlugin将css文件单独打包的时候,应注意从右往左的顺序</strong></p>
</blockquote>
<p>至此,完成了一个简单的react工程的配置。只包含有js和css相关的内容。</p>
<h1>工程优化</h1>
<blockquote>
<p><em>关于单页面工程的优化,有很多方向和方法。不管是生成环境构建还是懒加载,亦或是构建性能。通过环境变量来实现不同的配置和打包策略。根据工程的需求和复杂度不同,会有不同的解决方案,而随着现在页面越来越复杂,各个组件又经常变动。暂时还没有真正的完美的解决方案。</em>&nbsp;<br>在这里,我只能做一些常规性的优化。</p>

</blockquote>
<h5 id="ExtractTextWebapckPlugin">ExtractTextWebapckPlugin</h5>
<p>在上面的配置中,我们没有单独打包样式文件,样式文件会被打包在js里面。现在通过ExtractTextWebpackPlugin单独打包样式文件。<code>npm install -D extract-text-webapck-plugin</code>引入依赖,配置如下:</p>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>// module-&gt; rules
{
    test: /\.less$/,
    exclude: /node_modules/,
    include: /src/,
    // loader:['style-loader','css-loader']
    use: ExtractTextWebapckPlugin.extract({
      fallback:'style-loader',
      use: [
            {
                loader: 'css-loader',
                options: {
                  minimize: process.env.NODE_ENV === 'production',
                  importLoaders: 2,
                  localIdentName: '--',
                  modules:true
                }
            }, {
                loader: 'postcss-loader',
                options: {         // 如果没有options这个选项将会报错 No PostCSS Config found
                  plugins: (loader) =&gt; [
                        require('autoprefixer')(), //CSS浏览器兼容
                  ]
                }
            },{
                loader: 'less-loader',
                options: {
                  javascriptEnabled: true,
                }
            }],
      }
    )
},
// plugins 下新增
new ExtractTextWebapckPlugin({
    filename: 'css/-.css',
    // Setting the following option to `false` will not extract CSS from codesplit chunks.
    // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
    // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
    allChunks: true
}),</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<h5 id="为打包后的文件增加hash">为打包后的文件增加hash</h5>
<p>如果浏览器加载发现远端文件没有发生变化时,将会启用缓存,导致新修改的页面并没有同步,这时候为了避免缓存,我们就需要让每次打包后的文件有不同的文件名,以减少缓存。</p>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>// webpack.config.js -&gt; output
output: {
    path: path.join(__dirname, "dist"),
    publicPath: '/',
    filename: "js/-" + ".js",
    chunkFilename: "js/-" + ".js",
},</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<h5 id="打包静态文件">打包静态文件</h5>
<p>当页面图片较多时,会发送很多http请求,降低页面性能。url-loader引入图片编码,生成dataURI,把图片翻译成一串字符串。再把字符串打包到文件中,最终只需要引入文件就可以访问图片了。但是当图片较大时,编码会消耗性能。因此url-loader提供了一个limit参数,小于limit的文件会被转为dataURI,大于limit会使用file-loader传入。首先引入依赖<code>npm install -D file-loader url-loader</code>,配置如下:</p>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>// module-&gt;rules
{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'url-loader',
    options: {
      limit: 10000,
      name: 'static/img/..'
    }
},
{
    test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
    loader: 'url-loader',
    options: {
      limit: 10000,
      name: 'static/media/..'
    }
},
{
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
    loader: 'url-loader',
    options: {
      limit: 10000,
      name: 'static/fonts/..'
    }
},</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<h5 id="html-webpack-plugin">html-webpack-plugin</h5>
<p>这是一个 webpack 插件,为我们在生成有 hash 标识符的 css,js时非常方便,它需要我们指定一个模板,然后它会生成一个自动引入我们生成的文件的新模板。&nbsp;<br>首先安装依赖<code>npm install -D html-webpack-plugin</code>&nbsp;,webpack配置如下:</p>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>const HtmlWebPackPlugin = require('html-webpack-plugin')
// plugins下添加
new HtmlWebPackPlugin({
    template: './src/index.html',
    minify: {
      removeComments: true,
      collapseWhitespace: true,
      removeAttributeQuotes: true
      // more options:
      // https://github.com/kangax/html-minifier#options-quick-reference
    },
    filename: 'index.html'
}),</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<h5 id="devServer">devServer</h5>
<p>开发服务器配置如下:</p>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>// webpack.config.js
devServer: {
    // contentBase: path.join(__dirname, ""),
    contentBase: false, //since we use CopyWebpackPlugin.
    clientLogLevel: 'warning',
    publicPath: '/',
    hot: true,
    progress: true,
    overlay: { warnings: false, errors: true },
    historyApiFallback: {
      rewrites: [
            { from: /.*/, to: path.posix.join('/', 'index.html') },
      ],
    },
    // historyApiFallback: true,
    // quiet: true, // necessary for FriendlyErrorsPlugin
    compress: true,
    inline: true,
    port: 8083,
    host: '127.0.0.1',
    watchOptions: {
      poll: false,
    }
},</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<p>因为配置了contentBase = false,所以使用CopyWebpackPlugin,还是先安装依赖<code>npm install -D copy-webpack-plugin</code>,然后代码如下:</p>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>// plugins
// copy custom static assets
new CopyWebpackPlugin([
    {
      from: path.resolve(__dirname, './src/static'),
      to: 'static',
      ignore: ['.*']
    }
]),</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<h5 id="提取公共代码">提取公共代码</h5>
<p>利用webpack4的splitChunks来分割代码,配置如下:</p>
<div data-mode="js">
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
<pre>// webpack.config.js
//4.0配置
optimization: {
    /*splitChunks: {
      chunks: 'all',//"initial" | "async" | "all"
      cacheGroups: {
            default: false,
            vendors: false,
      },
    },*/
    /*splitChunks: {
      cacheGroups: {
            commons: {
                test: /[\\/]node_modules[\\/]/,
                name: "vendor",
                chunks: "all"
            }
      }
    }*/
    runtimeChunk: {
      name: "manifest"
    },
    splitChunks: {
      cacheGroups: {
            commons: {
                test: /[\\/]node_modules[\\/]/,
                name: "vendor",
                chunks: "all"
            }
      }
    }
},</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码"></span></div>
</div>
<p>&nbsp;</p>
</div>
<h1>&nbsp;</h1>
<h1><br><br></h1>
<div>来自为知笔记(Wiz)</div>
<p>&nbsp;</p>

</div>

</div><br><br>
来源:https://www.cnblogs.com/shaozhu520/p/11180381.html
頁: [1]
查看完整版本: 从零开始搭建一个react项目