小白学react之由FOUC引发的2回webpack变革

react-webpack.png

天地会赣州分舵注:随着微信应用号的有声有色,相信新一轮的APP变革即将产生。作为行行业内部职员,大家很应该去拥抱这几个样子。个中Reactjs相信是开发webapp的状元,那段时日将会经过改造官方实例alt-tutorial来上学Reactjs相关的学识。

上一篇《小白学react之页面BaseLayout框架及微信的坑》大家上学了什么为顺序Components提供多个基础的父框架组件来处理差别页面包车型客车Title的展现和Style样式的展现。

明天自身会尝试就融洽踩到的二个坑初阶,通过对webpack做进一步的修改,来上学更高级点的webpack相关的知识点。

根本须求化解的题材如下:

  • FOUC问题
  • 生儿育女和开发配置分离
  • 自动生成index.html页面模版文件
  • 哈希文件名
  • 理清废品营造文件

1. SCSS导入引发的标题 – FOUC之坑

明日在整理代码的时候境遇2个题材。正是首先加载页面包车型大巴时候页面会并发短期内的闪亮。相当于说,当大家第2次加载页面或然刷新首屏的时候,会在十分的短的年华内先出示一下之类页面:

home_li_flash_bef.png

能够看到地方的多少个li成分还是在没有利用上css在此以前的。然后快速的总体页面又回变成应用上css之后的页面:

home_li_flash_aft.png

用作小白,令人烦恼的一些是,碰着那种题材都不清楚应该谷歌(Google)什么重要字!尝试过各样”
reactjs first page refresh flash page reload sass import issue scss
apply issue…”等一大堆而无果。最后无意发现原先是贰个称呼FOUC (Fash Of
Unstyled Content)的题材。

flash of unstyled content (FOUC, also flash of unstyled text or
FOUT)[1][2] is an instance where a web page appears briefly with
the browser’s default styles prior to loading an external CSS
stylesheet, due to the web browser engine rendering the page before
all information is retrieved. The page corrects itself as soon as the
style rules are loaded and applied; however, the shift may be
distracting. Related problems include flash of invisible text (FOIT)
and flash of faux text (FOFT).

简单易行来说就是当样式表晚于结构性html
加载,当加载到此体制表时,页面将终止以前的渲染。此样式表被下载和分析后,将再一次渲染页面,也就出现了短短的闪光现象。

据称诱因有二种:

1,使用import方法导入样式表。
2,将样式表放在页面底部
3,有多少个样式表,放在html结构的不比岗位。

而解决方法就是:

消除措施:使用LINK标签将样式表放在文书档案HEAD中

因为大家的scss的体裁确实是通过import的方法给导入到我们的一一页面包车型大巴,所以自个儿深信不疑大家的诱因是第3种。以Home.jsx的样式表引入为例(上海教室中的七个li的渲染正是在Home页面中的):

import React from 'react'
import { Link } from 'react-router'
import  './Home.scss'
import BaseLayout from "./BaseLayout.jsx";

var Home = React.createClass({

    render() {
    return (
        <BaseLayout title="Home" style={{"backgroundColor":"white"}}>
          <div >
              <nav >
                <li className="home__tab__li"><Link to="/locations" >名胜古迹</Link></li>
                <li className="home__tab__li"><Link to="/about" >关于techgogogo</Link></li>
                  <div style={{clear:"both"}}></div>
                {this.props.children}
              </nav>
          </div>
        </BaseLayout>
    )
  }
})

module.exports = Home;

这便是说依照给出去的消除方案,大家是相应在运转的时候将scss提取出来改成1个单身的css文件,然后在index.html的页面模版司令员其引入。

投入提取出来的css文件叫做style.css,那么index.html的页面代码就活该改为:

<!doctype html>
<html >
  <head>
    <link rel="stylesheet" type="text/css" href="style.css" />
  </head>
  <body>
    <div id="ReactApp"></div>
  </body>
  <script src="bundle.js"></script>
</html>

那那里的题材就变成是何许生成这么些css文件?遵照大家后边的实战,整个源码在webpack打包之后实际就唯有叁个bundle.js文件而已。

2. webpack怎么着将scss打包成独立的css文件

为了将css打包成单身的一个文本,我们能够依靠3个叫做extract-text-webpack-plugin的webpack插件,我们能够从其github网页中查看到宗旨的音讯以及使用实例。

第②,大家须求将该模块安装上:

npm install extract-text-webpack-plugin --save-dev

然后我们需求在大家的webpack.config.js文件中程导弹入该模块:

let ExtractTextPlugin = require('extract-text-webpack-plugin');

进而实例化三个目的:

var extractSCSS = new ExtractTextPlugin('[name].css');

小心那里的[name]是webpack下面的一个至关心尊敬要字,代表entry中的块(chunk)的键。比如大家的entry的概念如下:

    entry: {
        app: path.resolve(__dirname, 'src/App.jsx'),
    },

那就是说这些[name]就是那里的“app”。缺省的话会是“main”。

然后大家须求在plugins中加入那么些实例:

 plugins: [
        new OpenBrowserPlugin({ url: 'http://localhost:8080' }),
        extractSCSS,
    ]

最终,将scss文件的loader改成如下:

{
    test: /\.scss$/,
    //loaders: ["style", "css?sourceMap", "sass?sourceMap"]
    loader: extractSCSS.extract('style', 'css!sass?sourceMap'),
}

到此,我们的webpack.config.js配置文件即便协理中校scss文件抽取成2个独自的css文件的行事了。大家在履行webpack打包的时候,那个工作就会活动达成:

npm run build

事成之后你会发现在build目录下活动会变卦一个叫做app.css的文本。

唯独,大家运营以前还要求做多二个作业,就是前方提到的内需将css文件放到index.html那一个模版文件的header部分:

<!doctype html>
<html >
  <head>
    <link rel="stylesheet" type="text/css" href="app.css" />
  </head>
  <body>
    <div id="ReactApp"></div>
  </body>
  <script src="bundle.js"></script>
</html>

终极咱们运转下边发号施令进行打包和运转express服务器:

npm run prod

开拓浏览器就能访问到大家耳熟能详的页面了。那时你会发觉无论是你怎么刷新网页,再也不会出现FOUC难点了。

理所当然,你也足以在开发形式下运行,bundle.js和app.css文件会活动在内部存款和储蓄器中生成,其效力是同一的:

npm run dev

3. 分手开发和生产安排

3.1 混乱的布局

唯独此地难点也亲临了,我们在做项目标长河中,开发格局和生育情势的打包进度反复是不雷同的。

比如说,大家付出方式中大家须求制定webpack-dev-server的一对参数,而在生育形式下我们是不须求的。

只是大家事先将一长串的参数放到了package.json的scripts下面:

  "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server  --inline --devtool eval --host 0.0.0.0 --progress --colors --hot --content-base ./build --history-api-fallback",
    "prod": "npm run build & node server.js"
  },

骨子里更规范点的做法应该是将其一大串参数放到webpack.config.js里面,比如大家放在config上面:

    devServer: {
        historyApiFallback: true,
        //hot: true,
        inline: true,
        progress: true,
        // display only errors to reduce the amount of output
        stats: 'errors-only',
        devtool: eval,
        colors: true,
        contentBase: "./build",

        // parse host and port from env so this is easy
        // to customize
        host: "0.0.0.0",// process.env.HOST,
        port: process.env.PORT
    },

那正是说那就重返了作者们地点提及的题材,那么些devServer只是在支付格局才须要的,在生产格局下是不要求的。

内部生产形式小编那边指的是package.json的scrip下的build命令,而支付方式指的是dev命令:

  "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server 
    "prod": "npm run build & node server.js"
  },

那便是说大家怎么才能正确的将开发配置和生育安顿分离开来呢?

3.2. 组别打包环境之npm_lifecycle_event

在webpack.config.js中要将开发配置和生产布署分离开来,首先大家就要获取到近来的景色终归是开发依旧生产。

生育和支付,首尽管反映在我们跑的授命是 “npm run build” 依旧”npm run
dev”, 也正是反映在package.json的scripts脚本的一声令下上。

那正是说大家在wepack.config.js中一旦能判断到用户跑的到底是哪些命令的话,大家就能实现这一点。

此时,特殊的环境变量npm_lifecycle_event就要登上舞台了。

npm 正在推行哪个 npm script, npm_lifecycle_event
环境变量值就会设为其值,比如

  • 履行 npm run dev 命令时,则其值为 “dev” ;
  • 执行 npm run build 命令时,其值为 “build” ;

所以,我们在webpack.config.js中首先须求做的正是获取到这一个变量:

const TARGET = process.env.npm_lifecycle_event;

区分别当前内需打包的是开发条件照旧生产条件之后,大家下一步要做的正是将它们的配备代码分开。

3.3. 布局分离之webpack-merge

配置分离我们会用到的是webpack-merge那么些包,大家先把它安装上:

npm install webpack-merge --save-dev

webpack-merge是特地用来拍卖webpack.config.js的配置文件分其他。它至关心重视要提供三个merge方法,来将逐一分开的配备项给合并在联合署名,详情请查看github

上面大家就足以参见其网站的言传身教,将付出和生育的包装配置给分离开来了,代码如下:

var path = require('path');
var OpenBrowserPlugin = require('open-browser-webpack-plugin');
const merge = require('webpack-merge');

const ExtractTextPlugin = require("extract-text-webpack-plugin");
var extractSCSS = new ExtractTextPlugin('[name].css');

const TARGET = process.env.npm_lifecycle_event;

var base = {
    entry: {
        app: path.resolve(__dirname, 'src/App.jsx'),
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname,"build"),
    },

    module: {

        loaders: [
            {
                test: /\.(js|jsx)$/,
                loader: 'babel',
                query: {
                    presets: ['es2015', 'react','stage-2']
                }
            }
        ]
    }
};

if(TARGET === 'dev' || !TARGET) {

    module.exports = merge(base, {
        devServer: {
            historyApiFallback: true,
            //hot: true,
            inline: true,
            progress: true,
            // display only errors to reduce the amount of output
            stats: 'errors-only',
            devtool: eval,
            colors: true,
            contentBase: "./build",

            // parse host and port from env so this is easy
            // to customize
            host: "0.0.0.0",// process.env.HOST,
            port: process.env.PORT
        },
        module: {
            loaders: [
                {
                    test: /\.scss$/,
                    loaders: ["style", "css?sourceMap", "sass?sourceMap"]
                }
            ]
        },

        plugins: [
            new OpenBrowserPlugin({url: 'http://localhost:8080'}),
        ]
    });
}

if(TARGET === 'build' || TARGET === 'prod') {

    module.exports = merge(base, {
        module: {
            loaders: [
                {
                    test: /\.scss$/,
                    loader: extractSCSS.extract('style', 'css!sass?sourceMap'),
                }
            ]
        },
        plugins: [
            extractSCSS
        ]
    });
}

从代码能够见到,整个经超过实际际上便是将计划文件拆分,以便能进行更灵敏的自由组合配置。这里有几点多少提一下的是:

  • 笔者们原本是唯有三个名为config的布置项,以往因为急需做安插分离,所以大家将该配置项改成base。意思是那是一个主导配备,由上面包车型大巴付出和生育布局后续和壮大
  • 尾随大家会判定当前跑的是还是不是是”npm run
    dev”命令或许是其他没有特意在package.json中钦点的一声令下,假设是的话,就会因此webpack-merge的merge方法,将开发环境下打包所需求的特定配置项进入到地点的中坚配备项地点,最终将配备给export出去。
  • 往下的生育环境安排分离和付出条件的布局分离类似,那就不多说了。

陈设分离消除后,大家下一个要化解的题材正是index.html页面模版文件的扭转。

4. 说了算index.html页面模版文件生成

为啥大家须求做那一个工作吗?因为,在此之前我们的index.html文件是手动创制的,在此之前的css文件也从未单身包装出来,那么大家今后有了独自的css文件从此,我们就必要手动的将那几个css文件加到index.html文件之中了。

如果那几个css文件的名字固定的话,那么我们只是修改3回也不曾多大标题。可是,借使像往下将要讲到的,打包出来的这一个css文件假如每一趟都不等同的话,那么大家是不恐怕每一次都去手动更新这么些html文件的了。

为了完成这一个指标,那里大家要求html-webpack-plugin的帮组。同理,大家先把这几个模块给安装上:

npm install html-webpack-plugin --save-dev

下一步正是随着github上的花色Readme去开始展览配置了。

率先,我们须求引入这几个模块:

const HtmlWebpackPlugin = require('html-webpack-plugin');

下一场,因为大家往下的HtmlWebpackPlugin配置在生成index.html的时候须要三个模板,所以我们先将本来的index.html该名为template.html,并修改内容如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
  <body>
    <div id="ReactApp"></div>
  </body>
</html>

那里我们去掉了css和bundle脚本的引入,因为那一个往下会自动生成并插入到这一个模版中生成新的index.html文件。

往下一步大家就须求去在webpack.config.js文件中继承配备该怎么着根据模版生成新的index.html文件了。

那一个配置是要放权配置的plugins上的。大家那里有base的布置,production的配置和development的配置,那么,大家那里只对生育环境控制新index.html文件的变化,所以大家只需求在生育布局下进行plugins配置就好了:

if(TARGET === 'build' || TARGET === 'prod') {

    module.exports = merge(base, {
        module: {
            loaders: [
                {
                    test: /\.scss$/,
                    loader: extractSCSS.extract('style', 'css!sass?sourceMap'),
                }
            ]
        },
        plugins: [
            extractSCSS,
            new HtmlWebpackPlugin({
                template: path.resolve(__dirname, "build/template.html"),
            })
        ]
    });
}

实则从该plugin的github网站上能够观望,该插件是支撑广大布局项的。因为我们那里的alt-tutorial演示项目比较简单,所以那边只用了template那个布局项,其余项私下认可。

暗许的话,该插件会:

  • 通过template这几个装置项找到大家的template.html文件,并在内部存款和储蓄器中拷贝一份到index.html文件之中,然后针对内部存款和储蓄器中的该index.html文件举行往下的控制
  • 将webpack生成的css文件引入到index.html的head部分
  • 将webpack生成的js文件引入到index.html的body部分
  • 保存index.html到硬盘上

就此,在大家运行上面包车型大巴通令之后:

npm run build

我们会发觉1个新的index.html文件将会在build目录下转移:

<!DOCTYPE html>
<html><head><link href="app.css" rel="stylesheet"></head>
  <head>
    <meta charset="UTF-8">
  <body>
    <div id="ReactApp"></div>
  <script type="text/javascript" src="bundle.js"></script></body>
</html>

5. Hash文件名以幸免浏览器Cashe导致难题

有了上边的html文件自动生成的建制之后,我们今日就足以将转变的js文件和css文件给hash起来了。

缘何我们需求给这么些文件的文件名做哈希呢?哈希的结果自然正是历次变更的文书的名字都会不平等了。可是名字不平等又是为哪出吧?

那至关心器重假若因为要处理浏览器cache导致的公文修改没有即时起效的难点。

譬如,我们近期透过浏览器访问大家的开发服务器机器的时候,会去加载bundle.js文件。那么下次自己有新的翻新,重新编写翻译之后,笔者再去通过浏览器去拜谒就会发觉更新没有选用上。因为,这一个时候浏览器发现bundle.js文件名尚未变,它就会选用原来cache起来的bundle.js继续提供劳动。那,就是怎么大家需求hash文件名。

事实上hash文件名在webpack的配置中国和北美洲常不难,大家只须要用上webpack中的此外二个主要字[chunkhash]就好了。

首先,我们修改base配置下的ouput项,将原来生成的bundle.js那一个文件的文本名如下:

    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname,"build"),
    },

build后转移的文书大将会是”app.xxxxx.js”,当中xxxxx代表的就是hash。

再就是,大家修改生成的css文件的文书名如下:

var extractSCSS = new ExtractTextPlugin('[name].[chunkhash].css');

那时候再运营:

npm run build

我们就会看到build文件夹下边会生成添加了哈希值的js和css文件,同时,大家会注意到index.html文件也会随着而变:

<!DOCTYPE html>
<html><head><link href="app.4f8080c8499588890c06.css" rel="stylesheet"></head>
  <head>
    <meta charset="UTF-8">
  <body>
    <div id="ReactApp"></div>
  <script type="text/javascript" src="app.4f8080c8499588890c06.js"></script></body>
</html>

6. 清理废物营造文件

将文件名展开hash的还要,会引入2个新的题材:每便当大家修改了文件后展开双重营造,因为文件内容变了,所以hash出来的文件名也决然产生变更。那么在创设多此次之后,大家的build目录下就会布满一大堆充满哈希值的公文名的垃圾文件。

那时候大家很有须要在创设前将其清理掉,以维持build目录的干干净净干爽。

此间引入3个新的webpack插件clean-webpack-plugin,使用格局也格外简单。

首先,大家跟过去同样将该插件给装上:

npm install clean-webpack-plugin --save-dev

接下来,大家在webpack.config.js中程导弹入该模块:

const CleanPlugin = require('clean-webpack-plugin');

末段对生产安插的plugins进行修改。因为支付环境中这么些文件都以在内部存款和储蓄器发生的,所以大家不要求展开任何配置。

new CleanPlugin(['build'], {
  root: path.resolve(__dirname,"./"),
  verbose: true,
  exclude: ['template.html','logo.png']
})

其中:

  • build: 所必要清理的公文夹。就是争持下边包车型地铁root路径下的build文件夹
  • root: webpack.config.js文件所在的相对路径
  • exclue: 不供给破除的文书列表

至此,大家成功了对全部webpack.config.js进行了相比大的变更,整个项指标营造也就更像模像样了。

7. 源码

git clone
https://github.com/kzlathander/alt-tutorial-webpack.git
cd alt-tutorial-webpackgit
checkout 07
npm install
npm run prod

同时

本文由世界会曲靖分舵编写,转载需授权,喜欢点个赞,吐槽请评论,进一步调换请关怀自个儿领域会凉州分舵以及《微信小程序开发》主题。

《未完待续》