webpack

前言

webpack前端工程中扮演的角色越来越重要,它也是前端工程化很重要的一环。本文将和大家一起按照项目流程学习使用wbepack,妈妈再也不用担心我不会使用webpack,哪里不会看哪里。这是一个由浅入深的文章。

工程化

这里是一个项目工程化,规范化的设置,如果是初次使用webpack的同学还是最后在看这一块知识

现在vue、react等脚手架都会自动将开发环境使用的webpack的配置文件和生产环境的配置文件分开,将压缩代码,添加hash控制版本等操作放在项目上线时运行,这样避免了在开发阶段打包时间过长的问题。比如像这样,把两个环境的配置文件分开。

 


下面来看下两个配置文件的内容(我是用的typescript开发react,内容请忽略)
开发环境:

 

 

 

生产环境:

 


可以看到,开发环境增加了几个插件,这样做的好处就是更加工程化,规范化,降低开发环境的打包时间,代码维护性也更高。

 

分开写配置文件就要涉及到使用命令执行不同的配置文件,我们可以使用npm的脚本命令,我们可以在package.json中找到scripts,添加如下命令"build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress"

给大家解释下这个命令的意思

  • NODE_ENV=production 就是将运行环境设置成生产环境
  • webpack --config 就是运行webpack的配置文件
  • ./webpack.production.config.js 是要运行的指定位置的文件,这个路径是相对根目录来说的
  • --progress 是编译过程显示进程百分比的

如果你不追求规范化和工程化,我们就写一个配置文件就好,这里没有硬性要求。下面我们来讲webpack的具体配置

开始

在我们对于webpack不是特别熟练的时候,我们可能不会写全配置文件,往往是用到什么再去添加,下面我们就按照这个步骤彻底学会使用webpack。

module.exports = {
    entry: './src/index.js' // 这里是项目入口文件地址
    ouput: {
        path: __dirname + "/dist", // 这里是项目输出的路径,__dirname表示当前文件的位置
        filename: "js/"+"[name].js" // 这里是生成文件的名称,可起你想要的名字
    }
}

loader

这就是我们最初一个骨架,下面我们再添加一些配置,比如你使用的是react,那么你就需要添加react的相关loader,这里以typescript编写的react为例。

module.exports = {
    entry: './src/index.js' // 这里是项目入口文件地址
    ouput: {
        path: __dirname + "/dist", // 这里是项目输出的路径,__dirname表示当前文件的位置
        filename: "js/"+"[name].js" // 这里是生成文件的名称,可起你想要的名字
    },
    module: {
        rules: [
            { test: /\.tsx?$/, loader: "awesome-typescript-loader" },
            { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
        ]
    },
}

css/css预处理语言(less、sass、stylus)

webpack是将一个个文件分拆成一个个模块(module)来进行编译打包的,我们所有的处理文件内容的东西都要放在module里,rules即规则。
rules里面的两个loader都是编译.tsx文件及处理错误信息的。

在你写好了组件之后,你需要开始编写样式,但无论是css还是使用less、sass等预处理语言,webpack都是无法直接处理的,我们安装并使用相应的loader。下面以less和css为例。

 {test: /\.(less|css)?$/, loader: ["style-loader", "css-loader", "less-loader"]}

webpack会按照从右到左的顺序执行loader,我们新解析less,之后进行css的打包编译。如果你不适用less等预处理语言,安装css-loader和style-loader即可。

  • style-loader 将css插入到页面的style标签
  • css-loader 是处理css文件中的url()等
  • less-loader 是将less文件编译成css

postcss解决css兼容问题

写到这里我们会突然想到一个点,就是css样式的兼容性问题,靠人工去写的话,你心里可能会有一句mmp不值当讲不当讲,哈哈,我们必须使用postcss来解决这个问题。

postcss是目前css兼容性的解决方案,会自动帮我们加入前缀,以使css样式在不同的浏览器能兼容,这里安装使用postcss-loader

{ test: /\.(less|css)?$/, loader: ["style-loader", "css-loader", "less-loader", "postcss-loader"]}

postcss-loader要写在最后(其实只要放在css-loader之后就可以),写到这你以为就可以了吗?只能说 too young,postcss解决兼容性问题主要靠的其实是它的插件autoprefixer,我们还需要在根目录写一个postcss.config.js的配置文件,如下

module.exports = {
    plugins: [
        require('autoprefixer')
    ]
};

写到这,我们就不用再担心css兼容性问题了,就想使用babel文件一样,这个文件会自动解析,我们不需要管它。

svg图片的使用

我们在开发时,往往会遇到一些图标图片在不同情况下会失真,以及资源过多,我们需要减小图标类图片的大小,这时我们就需要引入svg,国内可能都会去使用阿里的iconfont库,从而引入svg图标,解决上面的问题

我们打开下载的素材文件夹,发现里面有一些.woff、.svg、.eot的文件,我们要想使用svg的图标还必须依赖这些文件,这时webpack不支持这些文件,我们需要引入新的loader

{ test: /\.(woff|svg|eot|ttf)?$/, loader: "url-loader" }

下面我们就能愉快的使用svg图标了,不存在失真的情况,同时会很小

webpack-dev-server

写到这,我们可能不断的打包webpack了,太麻烦了,于是乎webpack-dev-server应运而生。它是webpack提供的服务器,我们使用npm i webpack-dev-server --save-dev来安装。
我们其实在命令行中敲击webpack-dev-server --open就可以开启,默认是localhost:8080开启,现在我们不需要在重复使用webpack命令打包,安装。

值得注意的是,webpack-dev-server打包的文件会存在内存中,所以在index.html中引入文件的时候就要如下,这里是默认输出文件是bundle.js

<script src="bundle.js"></script>

今天我们不重点讲webpack-dev-server,以后我会再写文章,深入的讲解其使用。

可能我们在开发阶段只用到了这几个功能,下面我们来讲一下项目上线的准备。

生产环境

优化

压缩js代码

我们打包完成的项目往往比较大,包含很多空格,占用了很大空间,这时我们要通过压缩js来减小文件体积。webpack自带了UglifyJsPlugin插件来压缩js代码,使用如下

plugins: [
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    ]

我们的插件统一放在export.modules = {}的plugins里面,它是一个数组,使用插件时new 一个实例即可。这里我们使用到webpack实例,所以要在配置文件头部引入webpack,即var webpack = require('webpack');

拆分文件

我们在使用的js库如vue或者react等的时候,webpack会将它们一起打包,react和react-dom文件就好几百k,全部打包成一个文件,可想而知,这个文件会很大,用户在首次打开时就往往会出现白屏等待时间过长的问题,这时,我们就需要将这类文件抽离出来。

externals: {
        "react": "React",
        "react-dom": "ReactDOM"
    },

这里我们会用到externals,它和plugins是平级。左侧key表示依赖,右侧value表示文件中使用的对象。比如在react开发中,我们常常会这样在文件头部写import React from 'react',这里大家可以和上面对号入座下。

这里我们就需要对这个文件进行单独的引入使用了,在index.html中添加如下代码

<script src="./node_modules/react/umd/react.xxx.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.xxx.js"></script>

写到这,我们就已经将文件拆分了。
不过,我们在项目上线的时候不可能会带有node_modules,所以我们就需要使用一个copy插件将react和react-dom文件复制出来

 new CopyWebpackPlugin([ // from是要copy的文件,to是生成出来的文件
            { from: "node_modules/react/umd/react.xxx.js", to: "js/react.min.js" },
            { from: "node_modules/react-dom/umd/react-dom.xxx.js", to: "js/react-dom.min.js" }
            { from: "public/favicon.ico", to: "favicon.ico" }
        ])

这样我们的index.html文件中就要写成下面这种形式

 

 

拆分css

我们也可以将css文件单独拆分出来,这样的好处就是打包的css文件我们可以放到cdn上,然后缓存到浏览器客户端中。这样就尽可能的减小文件的体积,以及不必要的资源重新加载,浪费带宽。

我们要先安装插件

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

配置文件添加对应配置

var ExtractTextPlugin = require("extract-text-webpack-plugin");

plugins里面添加插件

new ExtractTextPlugin("styles.css")
下面是具体的使用

module.exports = {
    // entry和output自动省略
    module: {
      loaders: [{
        test: /\.css$/,
        loader: ExtractTextPlugin.extract('style-loader',
        'css-loader!postcss-loader') // 这里我目前使用less还没有成功
      }]
    },
    postcss: function() {
      return [autoprefixer, cssnext, precss, cssnano]
    },
    plugins: [
      new ExtractTextPlugin('./css/[name].min.css') // 生成到css文件夹下
    ]
  }

webpack会将所有引用到的css文件打包,最终生成./css/[name].min.css文件。

图片处理

这里对图片进行base64进行转码同样是减小资源的体积

安装 url-loader

npm install url-loader --save-dev

在modules的rules里面添加

{
    test: /\.(png|jpg)$/,
    loader: 'url?limit=8192&name=images/[hash:8].[name].[ext]'
}

limit 设置一个阈值,小于这个值得图片就会自动启用 base64 编码的图片,大于这个值的图片会打包到name 这参数对应的路径,图片名称就会包括8位md5编码 name 对应文件本来名称,ext 对应扩展名

浏览器缓存资源

我们的后台会给资源设置Cache-Control: max-age=秒替代,来对资源进行缓存时间的设置,这使得我们在刷新页面之后会去缓存中加载资源,但是存在一个问题,就是,一旦我们更新版本之后,客户没有去清除缓存,同时缓存还没有过期的情况下,就无法加载到最新的资源。这时我们就需要hash值来进行版本控制

我们通常这样做

output: {
        path: __dirname + "/dist",
        filename: "[name][hash].js"
    }

给输出文件加上[hash]来添加hash值,这样就可以做到用户加载html里会去加载对应hash值得打包文件,比如下面这样

<script type="text/javascript" src="main3d1cb903f77dad5737e9.js"></script>

打包出来的js文件是这样

 


这样就能解决这个问题了。

 

还有最后一项

我们不可能每次都去手动复制一个index.html到打包好的dist文件中,我们会使用一款插件html-webpack-plugin
它可以自动添加html文件到dist文件中,同时它会自动添加js文件并带有hash值

引入插件

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

使用插件

new HtmlWebpackPlugin({
            template: path.join(__dirname, 'src/index.tmpl.html'),
            filename: 'index.html'
        })

这里给大家解释下,template是模板,我们在很多情况下,生产环境和开发环境不同,导致index.html引入的资源路径不同,这是为了改来改去,我们可以创建一个模板,它指定编译时我们copy的index.html文件。filename是最终生成的文件名。

模板文件如下

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <link rel="icon" href="favicon.ico">
    <title>Projection-Web</title>   
</head>
<body>
<div id="root"></div>
<script src="js/react.min.js"></script>
<script src="js/react-dom.min.js"></script>
</body>
</html>

生成的index.html文件如下

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <link rel="icon" href="favicon.ico">
    <title>Projection-Web</title>   
</head>
<body>
<div id="root"></div>
<script src="js/react.min.js"></script>
<script src="js/react-dom.min.js"></script>
<script type="text/javascript" src="js/main3d1cb903f77dad5737e9.js"></script></body>
</html>

下面是我打包编译的dist文件夹内容

 


下面是生产环境的配置文件(部分)

 

var CopyWebpackPlugin = require("copy-webpack-plugin");
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpack = require("webpack");
var path = require('path');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
    entry: "./src/index.tsx",
    output: {
        path: __dirname + "/dist",
        filename: "js/"+"[name][hash].js"
    },
    resolve: {
        extensions: [".ts", ".tsx", ".js", ".json"]
    },
    module: {
        rules: [
            { test: /\.tsx?$/, loader: "awesome-typescript-loader" },
            { enforce: "pre", test: /\.js$/, loader: "source-map-loader" },
            { test: /\.(less|css)?$/, loader: ["style-loader", "css-loader", "less-loader", "postcss-loader"] },
            { test: /\.(woff|svg|eot|ttf)?$/, loader: "url-loader" }
        ]
    },
    externals: {
        "react": "React",
        "react-dom": "ReactDOM"
    },
    plugins: [
        new CopyWebpackPlugin([
            { from: "node_modules/react/dist/react.js", to: "js/react.min.js" },
            { from: "node_modules/react-dom/dist/react-dom.js", to: "js/react-dom.min.js" },
            { from: "index.html", to: "index.html" },
            { from: "public/favicon.ico", to: "favicon.ico" }
        ]),
        new HtmlWebpackPlugin({
            template: path.join(__dirname, 'src/index.tmpl.html'),
            filename: 'index.html'
        }),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    ]
};

学好webpack,是一名现代前端开发工程师的基本素养。后续还会深入webpack,谢谢大家


作者:尼古拉斯李三
链接:https://juejin.im/post/5a068c2b5188255851322b8c
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

_________________________________________________________________________________________________________________

一些你不知道是什么意思的东西(题外话)

import path = require('path');

  • path.join
  • path.resolve
  • __dirname

path其实是node中的一个模块,下面我们就将一下这几个常见的东西。

path.join

path.join其实是对路径进行拼接。

const path = require('path');

let str1 = path.join('./path/./', './upload', '/file', '123.jpg');
console.log(str1); // path/upload/file/123.jpg

let str2 = path.join('path', 'upload', 'file', '123.jpg');
console.log(str2); // path/upload/file/123.jpg

使用了它之后就得到了一个拼接好的路径

path.resolve

它是绝对路径的操作

let myPath = path.resolve('path1', 'path2', 'a');
console.log(myPath); // E:/workspace/NodeJS/path1/path2/a

它的结果是绝对路径,不懂绝对路径和相对路径的同学去查下相关知识哦。
这部分到这就结束了,下面可能涉及到这几个东西的使用。

__dirname

使用__dirname变量获取当前模块文件所在目录的完整绝对路径
因为下面可能会碰到这几个东西,所以我们稍微简单提了一下。题外话到此结束,我们现在进入正题。

resolve配置

resolve.extensions

我们在编辑器上开发项目代码,但这个傻怂编辑器IDE功能并不是很强,每次import进来的文件都没有后缀名。如果你使用的是脚手架工具,你可能会发现一个有趣的地方,就是当我们引入js、jsx或者vue文件的时候,我们不需要加后缀就可以使用了。但是你写了一些less或者sass引入到文件中,也没有添加后缀,编译直接报错。这个其实就是resolve的问题。

resolve: {
    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: [".ts", ".tsx", ".js", ".json"]
}

对resolve进行配置能设置模块如何被解析。
这里的extensions就是后缀的使用,我这里默认是ts/tsx/js/json,这些文件在import时,不添加后缀是可以的,只需要在数组中添加你想要省略的后缀名就可以达到同样的效果。比如下面这种

extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx', '.less']

现在就算编辑器不给你添加后缀,你也不需要再加上后缀了,是不是省了很多事。

resolve.alias

这里是创建 import 或 require 的别名,来确保模块引入变得更简单

resolve: {
    alias: {
      Utilities: path.resolve(__dirname, 'src/utilities/'),
      Temp: path.resolve(__dirname, 'src/templates/')
    }
}

之前你引入src/template里面的1文件可能是这样

import 1 from '../src/template/1';

现在你可以这样写了

import 1 from 'Temp/1';

这里的路径还是具体看你的文件路径。不要照抄照搬哦。
这两项是我个人觉得使用频率比较多的,其他情况请去官方文档查看下。

全局变量的使用

使用全局变量进行url的替换

现在项目还是在开发阶段,你可能通过下面的接口获取信息

http://www.xxx.com/test/v3 + 具体接口

这个接口是放在测试服务器上的,但项目一旦上线要使用线上服务器

http://www.xxx.com/api/v4 + 具体接口

你于是冒出了一个很傻X的想法,本地开发或者测试时使用上面的接口,等到上线的时候再将它改掉。鬼鬼,咱不能这么秀。我给你提供一个好方法。

使用DefinePlugin插件来创建全局变量来解决这个问题

DefinePlugin允许你创建一个在编译时可以配置的全局常量,我们下面创建一个名为url的全局变量,如果你是将开发和生产环境的webpack配置文件分开,你可以这样写

开发环境
plugins: [
    new webpack.DefinePlugin({
        url: JSON.stringify('http://www.xxx.com/test/v3')
    })
]
生产环境
plugins: [
    new webpack.DefinePlugin({
        url: JSON.stringify('http://www.xxx.com/api/v4')
    })
]

如果你只有一个webpack配置文件,你也可以写成这样

plugins: [
    new webpack.DefinePlugin({
        url: process.env.NODE_ENV === 'production' ?
            JSON.stringify('http://www.xxx.com/api/v4') :
            JSON.stringify('http://www.xxx.com/test/v3')
    })
    // 这里其实涉及到一个问题,就是你在生产环境的时候必须增加命令修改process.
    // env.NODE_ENV = production,否则上面的代码是不生效的
    // 在package.json的scripts对象中可以使用,使用方法见我上一篇webpack的文章
]

由于这个变量必须包含字符串引号,所以你要么使用'"你的变量内容"', 或者使用 JSON.stringify('你的变量内容')这种形式。
现在你在项目中的接口url就可以写成这样了

`${url}/接口信息` // es6的字符串模板应该都懂吧?

你console.log(url)也是可以的哦,现在我们就解决了这个恶心的问题。
但是还有更恶心的问题等着我们,哈哈。真滴烦!!!
如果你的项目是脚手架搭建,往往会有eslint,eslint不进行设置是没办法使用这个全局变量的,找到eslint配置文件,添加如下代码

"globals": {
    "go": true
}

多入口文件打包

为什么要设置多文件入口打包

在很多情况下,我们都是用当下流行的框架进行web开发,比如vue、react。在开发一段时间过后,测试ok,我们准备build项目了,但是打包之后文件是4.5MB,这玩意对pc或者移动来说都不是一个很好的体验。如果我们不去管它,那每次我们改版或其他的一些情况,用户都需要去重新加载4.5mb的文件,哪怕你只是修改了一行代码。

多入口实现第三方库文件的单独打包

使用多入口文件配合插件解决此问题

entry: {
    vendor: ['react', 'react-dom'],
    app: "./src/index.tsx"
}

这里我们设置了两个入口,一个是app,就是我们传统使用的入口文件。而vendor使用的是一个数组,我们把react和react-dom单独提取出来进行打包。这些库我们基本上是不会改动源代码的,如果我们把它们单独打包出来,即便我们修改了项目的代码,react和react-dom的代码都不需要变,这时浏览器都直接读取vendor文件的缓存就可以了,减少了每次加载资源的体积,增强了用户体验。

配合插件CommonsChunkPlugin使用

只是增加入口文件是不管用的,我们需要使用插件把vendor文件从app文件中剥离出来

plugins: [
    new webpack.optimize.CommonsChunkPlugin('vendor')
]

现在我们已经把vendor和app文件分割了。这里只是举了一个简单的栗子,小伙伴可以根据自己的需求自己进行配置。

我们也可以把公共组件进行一个单独的打包,这里不再赘述,感兴趣的小伙伴可以自己试验哦。

html-webpack-plugin

之前的文章其实已经介绍过这个插件了,但这次我们稍微具体的说一下。我们使用脚手架生成的index.html其实你是找不到script标签引入js文件的,可能你也想这样做,自己动手引入真的麻烦。html-webpack-plugin来帮你

plugins: [
    // Generates an `index.html` file with the <script> injected.
    new HtmlWebpackPlugin({
      inject: true, // 这个配置项为true表示自动把打包出来的文件通过自动生成script标签添加到html中
      template: index.html, // 模板文件,其实如果没有特殊要求,可以考虑就是用原本的html文件,不再单独创建模板。
      minify: { // 压缩的配置,感兴趣的同学意义自己查下意思
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true,
      },
    })
]

你的模板html文件是这样

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
</head>
<body>
<div id="example"></div>

<!-- Main -->
</body>
</html>

通过使用上面的html-webpack-plugin配置它就变成了这样

<!DOCTYPE html><html><head><meta charset="UTF-8"/><title>Hello React!</title></head><body><div id="example"></div><script type="text/javascript" src="vendor.19786c9df38012fdca96.js"></script><script type="text/javascript" src="app.19786c9df38012fdca96.js"></script></body></html>

这样其实就是和你用脚手架搭建的一毛一样了。

讲一些项目中会出现的问题--devServer的使用

我们在使用webpack时,常常会用到webpack-dev-server。它给我们提供了一个server,使我们的项目能跑server上。

historyApiFallback

现在项目在本地正常运行,你看了下地址栏

localhost:8080/#

leader说#是什么鬼?必须去掉,你见过谁的网址带#?
这个#其实是hash路由进行路由跳转的依托。它是可以去掉的。react4是使用BroswerRouter替换HashRouter即可,vue的话同学去查一下即可。去掉#之后我们使用的就是h5的history模式进行路由跳转了。

你以为这样就行了么?

但是老板又有新要求了,我们的页面不是放在根目录下。网址是这样www.xxx.com/xxx,这个时候你要想页面放在服务器上能正常使用就需要在index.html添加base标签。
你信心满满的进行试验,发现,我擦,报错了,连页面都找不到了。

 


这个原因是因为现在在这个路径下我们是找不到资源文件,会报404的问题,这个时候我们必须要设置

 

devServer: {
    historyApiFallback: true
  }

这个东西就是告诉webpack-dev-server,再找不到文件的时候默认指向index.html,底层的东西我并没有去深入查询,感兴趣的同学可以去查下。

有些情况下,我们可能会起不止一个服务,这个时候端口往往就会冲突,添加port属性,修改下端口即可解决冲突问题。

devServer: {
    historyApiFallback: true,
    port: 1234
  }

现在,地址就变成了localhost: 1234了。
devServer其实还有很多配置项,感兴趣的同学可以去官网查看

webpack的热替换问题

热替换俗称HMR,即只更新你修改的局部内容,而不刷新整个页面,大大提高开发效率。这个只能在开发时使用哦,下面简单说一下用法。后面直接说react和vue的使用。

 const webpack = require('webpack');

  module.exports = {
    entry: {
      app: './src/index.js'
    },
    devServer: {
     hot: true
    },
    plugins: [
     new webpack.HotModuleReplacementPlugin()
    ]

下面是index.js文件需要的配置(这里觉得比较鸡肋,因为如果你不把js全写在一起,你就要每个文件都要添加这个东东。)

+ if (module.hot) {
+   module.hot.accept('./app.js', function() {
+     console.log('Accepting the updated printMe module!');
+     printMe();
+   })
+ }

module.hot.accept 接受两个参数,第一个参数是修改的文件,第二个是会掉函数。这里只要app.js修改,就会触发回调。

我们大多数情况下是使用react或者vue的,在这种情况下,你使用webpack的HMR是不起作用的。因为它无法保存这些框架的状态。
react和vue给我们提供了解决方案

  • react-hot-loader
  • vue-loader

    同学们可以在npm中搜索这两个东西的用法,都有对应的使用。

此部分webpack配置要去npm找到对应的loader看,很简单。我主要讲我当时很困惑的一点。
我主要讲一下react的,就是在使用路由和redux的情况下,我们的index.js文件应该怎么写。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { AppContainer} from 'react-hot-loader'
import registerServiceWorker from './registerServiceWorker';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducers from "./reducer";
import "./index.css";
import App from "./App";
import './style/style';

// redux的配置,可以忽略
const store = createStore(reducers, compose(
    applyMiddleware(thunk),
    window.devToolsExtension ? window.devToolsExtension() : ()=>{}
));

registerServiceWorker();

使用AppContainer包裹根组件即可

const render = Component => {
  ReactDOM.render(
      <AppContainer>
          <Component store={store}/> 
      </AppContainer>,
      document.getElementById('root')
  )
};
render(App);
// App作为内容的根组件,redux和router全部放在里面,只要有内容修改,就调用render函数
if (module.hot) {
    module.hot.accept('./App', () => { render(App) })
}

App文件

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { Provider } from 'react-redux';

import Second from "./second";
import Third from "./Third";
import Header from "./header";
import First from "./first";
class App extends Component {
  render() {
    return (
      <Provider store={this.props.store}>
          <BrowserRouter className="App">
              <div>
                  <Header />
                  <Switch>
                      <Route exact path="/" component={First}/>
                      <Route path="/second" component={Second}/>
                      <Route path="/third" component={Third}/>
                  </Switch>
              </div>
          </BrowserRouter>
      </Provider>
    );
  }
}

export default App;

我告你,现在你开发,根本不需要页面刷新,超爽的。修改哪里,哪里变化。谢谢大家


作者:尼古拉斯李三
链接:https://juejin.im/post/5a17a9ef51882540f3633bff
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @ 2017-11-25 13:38  漂流记  阅读(580)  评论(0编辑  收藏  举报