GVKun编程网logo

【webpack系列】从零搭建 webpack4+react 脚手架(二)(webpack搭建react项目)

2

此处将为大家介绍关于【webpack系列】从零搭建webpack4+react脚手架的详细内容,并且为您解答有关二的相关问题,此外,我们还将为您介绍关于3项目脚手架搭建npm,webpack-cli及

此处将为大家介绍关于【webpack系列】从零搭建 webpack4+react 脚手架的详细内容,并且为您解答有关的相关问题,此外,我们还将为您介绍关于3项目脚手架搭建npm, webpack-cli及webpack、vue+webpack4 脚手架搭建、webpack 配置react脚手架(二):热更新、webpack 配置react脚手架(五):mobx的有用信息。

本文目录一览:

【webpack系列】从零搭建 webpack4+react 脚手架(二)(webpack搭建react项目)

【webpack系列】从零搭建 webpack4+react 脚手架(二)(webpack搭建react项目)

html文件如何也同步到dist目录?bundle.js文件修改了,万一被浏览器缓存了怎么办?如何为导出的文件加md5?如何把js引用自动添加到html?非业务代码和业务代码如何分开打包?如何搭建开发环境?如何实现开发环境的热更新?

在上一节我们已经搭建了一个最基本的webpack环境, 这一节我们带着上节的一些疑问,继续优化我们的react工程。
 
1.整合html-webpack-plugin
 
public的index.html应该自动编译到dist目录,并且所有的js引用是自动添加的。你可以使用html-webpack-plugin插件来处理这个优化。
(1)安装html-webpack-plugin:
npm install --save-dev html-webpack-plugin
(2)在webpack.prod.conf.js中配置plugins属性。
    const merge = require(webpack-merge);
    const baseWebpackConfig = require(./webpack.base.conf.js);
    const HtmlWebpackPlugin = require(html-webpack-plugin);

    module.exports = merge(baseWebpackConfig,{
        mode: production,plugins: [
            new HtmlWebpackPlugin({
                template: public/index.html,inject: body,minify: {
                    removeComments: true,collapseWhitespace: true,removeAttributeQuotes: true
                },})
        ]
    });
(3)删除index.html中手动引入的script标签

index.html的代码应该是这样的:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <Meta charset="UTF-8">
        <title>从零开始搭建react工程</title>
    </head>
    <body>
    <div id="root"></div>
    </body>
    </html>
(4)重新执行编译
npm run build

查看dist文件夹,index.html也被加载进来了,并且已经自动加上了script标签。

 

2.为导出的js文件添加内容hash
 
当我们的业务有修改,bundle被重新打包,很可能在客户的电脑上并没有奏效,你告诉客户,应该是被缓存了,需要刷新浏览器,清理下浏览器缓存。这也许能解决问题,但是太糟糕了,我们有更好的方式,让导出的js文件加上文件hash,从而每次修改,转译出的js文件名称都不相同,那么js文件当然不会被缓存了。
添加文件hash的方法很简单,只要修改 output.filename 属性即可,这里我们做一个小小的优化,把导出的文件存放在js目录下,并且直接使用name+chunkhash的方式来命名。
filename: "js/[name].[chunkhash:16].js"
其中,name就是模块名称,我们在entry中进行过配置,chunkhash是文件内容的hash,webpack默认采用md5的方式对文件进行hash。16是hash的长度,如果不设置,webpack会设置默认值为20。
现在,你的webpack.prod.conf.js文件看起来应该是这样:
    const merge = require(webpack-merge);
    const baseWebpackConfig = require(./webpack.base.conf.js);
    const HtmlWebpackPlugin = require(html-webpack-plugin);

    module.exports = merge(baseWebpackConfig,output: {
            filename: "js/[name].[chunkhash:16].js",},})
        ]
    });

 

3.编译前清理dist目录
 
现在,如果你修改了你的业务代码,然后重新编译,你会发现在dist/js文件夹出现多个js文件。因为导出的js文件hash已经不相同,每次编译都会增加新的js文件,原来的文件没有被删除。所以,我们需要在编译前进行清理dist文件夹。
(1)安装clean-webpack-plugin
npm install --save-dev clean-webpack-plugin
(2)修改webpack.prod.conf.js,使用clean-webpack-plugin
    const merge = require(webpack-merge);
    const baseWebpackConfig = require(./webpack.base.js);
    const HtmlWebpackPlugin = require(html-webpack-plugin);
    const CleanWebpackPlugin = require(clean-webpack-plugin);

    module.exports = merge(baseWebpackConfig,}),new CleanWebpackPlugin([../dist],{ allowExternal: true })
        ]
    });
(3)执行试试看
npm run build

编译过程,注意查看控制台输出,你会发现webpack删除了dist目录。

 

4.非业务代码单独打包
 
在build结束,webpack会在终端显示打包文件的大小,我们可以看到这个app.js包大概在96.9KB

分享图片

 

随着我们业务代码的增加,这个包将会越来越大。
你每次发布,这个文件都会被重新下载。你的代码有修改,用户需要重新下载无可厚非。可是,你别忘了这个app.js内还包含了很多不变的代码,比如react,react-dom。我们需要把这些不变的代码分开打包。
在webpack.base.conf.js,我们添加一个入口配置。entry有2个入口。

    entry: {
            app: ./app/index.js,framework:[react,react-dom],

重新执行npm run build,再看看。

的确,react和react-dom 被编译成framework.js。可是,你会发现,app.js并没有减少,还是96.9KB。因为我们还缺少一步,就是抽离app.js中公共的代码。
webpack3版本是通过配置CommonsChunkPlugin插件来抽离公共的模块。webpack4版本,官方废弃了CommonsChunkPlugin,而是改用配置optimization.splitChunks的方式,更加方便。
在webpack.prod.conf.js增加如下代码:

    optimization: {
            splitChunks: {
                chunks: "all",minChunks: 1,minSize: 0,cacheGroups: {
                    framework: {
                        test: "framework",name: "framework",enforce: true
                    }
                }
            }
        }

cacheGroups对象,定义了需要被抽离的模块,其中test属性是比较关键的一个值,他可以是一个字符串,也可以是正则表达式,还可以是函数。如果定义的是字符串,会匹配入口模块名称,会从其他模块中把包含这个模块的抽离出来。name是抽离后生成的名字,和入口文件模块名称相同,这样抽离出来的新生成的framework模块会覆盖被抽离的framework模块,虽然他们都叫framework。
重新执行npm run build你看到app.js的体积变小了 才1kb。

分享图片

注意查看生成的文件的hash,接下去我们随意修改app/index.js的代码。重新执行npm run build编译。看看编译后的结果:

分享图片

看到了吗,app的hash发生了改变(它不能被浏览器缓存),而framework没有改变(它会被浏览器缓存),这达到了我们预期的结果。

5.压缩js文件
 
打开build后的js文件看看,js文件没有被压缩。在prod环境,我们希望js已经被压缩了,这样做的好处是减少文件体积,更快地被用户加载。js压缩,用到了uglifyjs-webpack-plugin,在optimization内进行配置。
(1)安装uglifyjs-webpack-plugin
npm install --save-dev uglifyjs-webpack-plugin
(2)在webpack.prod.conf.js页面上引入
const UglifyJSPlugin = require(uglifyjs-webpack-plugin);
(3)optimization内配置minimizer参数
    minimizer: [
        new UglifyJSPlugin()
    ],

你的optimization参数现在应该是这样:

     optimization: {
            minimizer: [
                new UglifyJSPlugin()
            ],splitChunks: {
                chunks: "all",cacheGroups: {
                    framework: {
                        priority: 200,test: "framework",enforce: true,reuseExistingChunk: true
                    },vendor: {
                        priority: 10,test: /node_modules/,name: "vendor",reuseExistingChunk: true
                    }
                }
            }
        }
(4)重新执行npm run build
npm run build

 

6.整合dev环境
 
我们不可能每次修改代码都去手动编译,等编译出来再去打开文件查看效果。webpack提供了开发环境服务,并且支持热更新,这在开发环境是非常有必要的。
webpack-dev-server这个模块提供了开发服务的支持,通过在webpack.dev.conf.js文件配置devServer可以方便地整合webpack-dev-server。
(1)安装webpack-dev-server
npm install --save-dev webpack-dev-server
(2)在build中添加webpack.dev.conf.js文件
    const path = require(path);
    const merge = require(webpack-merge);
    const baseWebpackConfig = require(./webpack.base.conf.js);
    const HtmlWebpackPlugin = require(html-webpack-plugin);
    const webpack = require(webpack);

    module.exports = merge(baseWebpackConfig,{
        mode: development,output: {
            filename: "js/[name].[hash:16].js",minify: {
                    html5: true
                },hash: false
            }),new webpack.HotModuleReplacementPlugin()
        ],devServer: {
            port: 8080,contentBase: path.join(__dirname,../public),compress: true,historyApiFallback: true,hot: true,https: false,noInfo: true,open: true,proxy: {}
        }
    });

HotModuleReplacementPlugin是webpack热更新的插件,设置devServer.hot为true,并且在plugins中引入HotModuleReplacementPlugin插件即可。
还需要注意的是我们开启了hot,那么导出不能使用chunkhash,需要替换为hash。

(3)在package.json增加一个npm scripts
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
(4)执行dev命令
npm run dev

打开 http://localhost:8080 查看,你可以尝试改动index.js的代码,浏览器自动更新了,说明整合webpack-dev-server成功。 你可能注意到,对于css相关的技术栈,我只字未提,别急,下一节我们会详细针对css相关的技术栈进行整合。

3项目脚手架搭建npm, webpack-cli及webpack

3项目脚手架搭建npm, webpack-cli及webpack

1.所有需要使用NPM 的项目都需要使用npm init进行初始化

webpack.github.io/docs/configuration.html

 

使用npm install进行安装的话,会在根目录下产生一个node_modules的文件夹,

所有你npm 的包都会放置于此

-g全局安装

--registry= 指向安装,可以把安装引导到国内环境的安装

2.webpack模块化文件

安装 npm install webpack@1.15.0 --save-dev可以初始化项目中的安装

版本

2.操作

cnpm install webpack@1.15.0 --save-dev

 

 

 

webpack在使用包的时候先使用本地的包,没有的时候才选择使用全局的安装包

webpack使用1.15.0而不使用2.0是因为其中有default的关键字和ie8以下的浏览器有冲突导致出现错误。

 

--save-dev可以把项目的配置记录在package.json中-包括版本

 

3.webpack.config.js

3.loader

 

4.常用命令工具

webpack 常用于调试代码用

wepack -p只用于做线上打包时,会把所有文件都做最小话压缩

webpack --watch 用作监听文件的改变,和自动编译,一般用于开发过程

webpack --config webpack.config.js 改变默认的配置文件位置

删除 rm -r -f node_modules

5.webpack-dev-server

作用:前端开发的服务器

webpack --watch不能刷新浏览器基本不用

 

 

3-4npm和webpack的初始化

  1. npm init 设置各种文件配置
  2. sudo npm install webpack -g 全局的webpack安装
  3. npm install webpack@1.15.0 --save-dev 项目目录安装webpack

    查看版本webpack -v 是否安装成功

    某些情况无法查看的情况需要 npm install webpack-cli -g

 

4.文件的建立

 

app.js

cats = require(''./cats.js'');

console.log(cats);

 

cats.js

var cats = [''dave'', ''henry'', ''martha''];

module.exports = cats;

 

5.打包文件的建立

注意webpack的版本可能会导致打包失败,而版本的额设置需要同步的在package.json中设置好,

然后npm init,再重新安装webpack

webpack ./src/page/index/index.js ./dist/app.js

把index.js打包进app.js中去

较为麻烦

直接把打包文件设置好进行

 

webpack.config.js

 

const path = require(''path'');

 

module.exports = {

entry: ''./src/page/index/index.js'',

output: {

path: path.resolve(__dirname, ''./dist''),

filename: ''app.bundle.js''

}

};

 

webpack -p进行打包

 

6.webpack对脚本的处理

 

 

1)jquery的使用

npm install jquery --save

然后在index中设置好

 

以上安装方法无法在全局中使用

卸载后重新安装,某些情况下无法供全局使用 npm uninstall jquery --save

  1. 直接在index.html中引入,就成了全局的jq了

然后在index.js设置

 

删除掉jqury npm uninstall jquery --save

2.js多入口的问题 entry

module.exports = {

entry: {

''index'':[''./src/page/index/index.js''],

''login'':[''./src/page/login/index.js''],

},

output: {

path: path.resolve(__dirname, ''./dist''),

filename: ''js/[name].js''

}

};

3.模块化引入jQuery

index.js

var $$ = require(''jquery'');

$$(''body'').html("HELLO JQ*****");

webpack.config.js

externals:{

''jquery'':''window.$''

}

 

此处注意一下两个文件的先后顺序

4.使用CommonsChunkPlugin提取公共代码

特别注意webpack 4已经更改了该插件的支持

关于版本的问题

***1.安装 在全局下安装:npm install webpack -g

安装指定版本:npm install web

pack@<version> -g 例如:npm install webpack@3.4.1 -g

如果只是用来练习全局安装就可以了,一开始装了个4.8.3版本的,node也是最新的npm也好着,一做项目就出现错误,4.*.*版本以上还需要安装另外一个东西,具体的可去webpack官网看。

最后只能把webpack删除,重新装了一个指定版本的,才没有什么问题了。

***2.删除 在全局下删除 npm uninstall webpack -g

最好将项目目录下的node-modules一起删除,否则会有残留文件影响下一次的结果。

 

特别注意在安装全局的webpack和项目的webpack时注意版本的一致性3.6.0

webpack-cli的版本不能比webpack的版本高,不然会出现问题

5.将不同的公共代码打进同一个文件中

webpack.config.js打包入口文件

./src/page/common/index.js''和 ''index'':[''./src/page/index/index.js''],打包进base.js文件中、

 

entry: {

''common'':[''./src/page/common/index.js''],

''index'':[''./src/page/index/index.js''],

''login'':[''./src/page/login/index.js'']

},

脚本

plugins: [

new webpack.optimize.CommonsChunkPlugin({

name: "common",

filename: "js/base.js"

})

]

 

原理-先定义一个公共的module.js》common/index.js 引入require(''../module.js'');》打包

 

vue+webpack4 脚手架搭建

vue+webpack4 脚手架搭建

 1, vue 中 h => h(App) 的含义:

//render: h => h(App) 是下面内容的缩写:

render: function (createElement) {
    return createElement(App);
}
//进一步缩写为(ES6 语法):

render (createElement) {
    return createElement(App);
}
//再进一步缩写为:

render (h){
    return h(App);
}
//按照 ES6 箭头函数的写法,就得到了:

render: h => h(App);

 

2 文章中 vue 组件中如果引入了样式,一定要引入css

{
        test:/\.css$/,
        use:[
          ''style-loader'',
          ''css-loader''
        ]
}

 

 

----

参考文章:

1: 从零开始配置 webpack4 + vue2.x (一)

2: 从零开始的 webpack4 + vue2.x

webpack 配置react脚手架(二):热更新

webpack 配置react脚手架(二):热更新

下面继续配置 webpack dev server    hot module replacement:

首先配置dev-server     安装     npm i webpack-dev-server -D 

const isDev = process.env.NODE_ENV === ''development''

const config = {
    entry:{},
    output:{},
    plugins:{}
}

if(isDev){
    config.devServer = {
    host: ''0.0.0.0'', //设置 0.0.0.0 的host 可以访问 localhost,127.0.0.1,本季ip来访问
    contentBase: path.join(__dirname, ''../dist''), //因为,devserver是服务打包后的文件,所以和output的path 保持一致即可
    port: ''8888'',
    hot: true,
    overlay: {
      errors: true //server有任何的错误,则在网页中 蒙层加提示
    }
  }
}

module.exports = config;

 

修改json文件: "dev:client": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.client.js", 

其中 cross-env 是兼容了win mac linux的NODE_ENV,也是一个安装包:  npm i cross-env -D 

然后 npm run dev:cient 即可以启动服务 localhost:8888;

发现 app.js是无法获取到的,其路径为: http://localhost:8888/public/app.js 可以看出是多了一层 public;

根据server的配置项:contentBase 是把dev-server放在了dist目录下,开启的服务器。则 locahost:8888 相当于 dist目录,而之前output配置的输出文件前面是有路径 public的,所以 dev-server也需要增加这个配置:

if(isDev){
    config.devServer = {
    host: ''0.0.0.0'', //设置 0.0.0.0 的host 可以访问 localhost,127.0.0.1,本季ip来访问
    contentBase: path.join(__dirname, ''../dist''), //因为,devserver是服务打包后的文件,所以和output的path 保持一致即可
    port: ''8888'',
    hot: true,
    overlay: {
      errors: true //server有任何的错误,则在网页中 蒙层加提示
    },
    publicPath: ''/public/'', //增加公共路径,对应着output的 publicPath
    historyApiFallback: {
      index: ''/public/index.html'' //这里是给本地服务器增加功能:因为是单页面应用,如果刷新页面,或者访问不到路由,则跳转到首页
    },
  }
}

注意:一定要把打包生成的dist目录删除掉,在执行 npm run dev:client 这时因为,服务器会先检测本地磁盘是否有dist目录,如果有就会调取这里面的文件!!

===================华丽的分割线

接下来配置 热更新 hot,

Q:webpack-dev-server 已经是热加载,为何还要在 react 项目还要安装 react-hot-loader 呢?

A:其实这两者的更新是有区别的,webpack-dev-server 的热加载是开发人员修改了代码,代码经过打包,重新刷新了整个页面。而 react-hot-loader 不会刷新整个页面,它只替换了修改的代码,做到了页面的局部刷新。但它需要依赖 webpack 的 HotModuleReplacement 热加载插件 (参考文章: react使用react-hot-loader实现局部热更新)

首先在 .babelrc 文件中增加 对react hot更新的配置:

{
  "presets": [
    ["es2015", { "loose": true }],
    "react"
  ],
  "plugins": [ "react-hot-loader/babel"] //使用babel的情况下,添加 react-hot-loader,支持react 热更新
}

 安装包: npm i react-loader@next -D //教程中这里是最新的版本,尚未正式版,开发的时候注意版本

修改app.js 入口文件:

import React from ''react''
import ReactDOM from ''react-dom''
import App from ''./App.jsx''

ReactDOM.hydrate(<App />,document.getElementById(''root''));

if (module.hot) {
  module.hot.accept(''./App.jsx'', () => {
    const NextApp = require(''./App.jsx'').default  
    ReactDOM.hydrate(<NextApp />,document.getElementById(''root''))
  })
}

// module.hot 监听到 app.jsx发生变化之后,重新获取 app.jsx 为NextApp 然后重新渲染;

修改package.js文件:

const webpack = require(''webpack''); //因为用到了webpack下的包  HotModuleReplacementPlugin
const config ={

}
if(isDev){
    config.entry=[
        ''react-hot-loader/patch'',  //入口文件中要把 hot 打包进去
        path.join(__dirname,''../client/app.js'')
    ],
    config.devServer = {
        host: ''0.0.0.0'', 
        contentBase: path.join(__dirname, ''../dist''), 
        port: ''8888'',
        hot: true, //打开这里
        overlay: {
          errors: true 
        },
        publicPath: ''/public/'', 
        historyApiFallback: {
          index: ''/public/index.html''
        }
    }
    config.plugins.push(new webpack.HotModuleReplacementPlugin) //增加了这里
}

最后还要返回来在 app.js 入口文件中配置:

import React from ''react''
import ReactDOM from ''react-dom''
import { AppContainer } from ''react-hot-loader''
import App from ''./App.jsx''

ReactDOM.hydrate(<App />,document.getElementById(''root''));

const root = document.getElementById(''root''); 
const render = Component => {
    ReactDOM.hydrate(
        <AppContainer>
            <Component/>
        </AppContainer>,
        root
    )
}

render(App);
if (module.hot) {
  module.hot.accept(''./App.jsx'', () => {
    const NextApp = require(''./App.jsx'').default  
    render(NextApp);
  })
}

这样才能热更新!

=================================服务端更新配置

上面书写了客户端的热更新,并且热更新的文件都存在内存中,所以服务端不能再从 dist文件夹下获取依赖的 js和 html文件,因此,服务端的js文件也需要区分是否是dev模式:

const express = require(''express'')
const ReactSSR = require(''react-dom/server'');
const fs = require(''fs'')
const path = require(''path'')
const app = express();

const isDev = process.env.NODE_ENV === ''development''; //在这里定义
if(!isDev){
    const serverEntry = require(''../dist/server-entry'').default;//引入的是服务端的配置打包后的js文件
    const template = fs.readFileSync(path.join(__dirname, ''../dist/index.html''), ''utf8'')//同步引入客户端打包生成的 html 文件,如果不使用 utf8 则是buffer文件
    app.use(''/public'', express.static(path.join(__dirname, ''../dist''))); //给静态文件指定返回内容,这里给piblic文件夹下的内容返回的是静态文件的dist文件夹
    app.get(''*'', function (req, res) {
      const appString = ReactSSR.renderToString(serverEntry);
      res.send(template.replace(''<!--app-->'',appString)) //用返回的js文件替换掉模板中的<app>,然后发送的是模板文件
    })    
}else{
    //util 文件夹下的 dev.static.js
    const devStatic = require(''./util/dev.static.js'');
   devStatic(app); //之所以这里把 app 传递进去,是因为app是 express(),我们可以在新建的文件中继续使用 app.get、app.send 等函数 } app.listen(
3333, function () { console.log(''server is listening on 3333'') })

根据以上代码可知,把原来的从dist目录下获取文件的代码放在了 不是 dev模式下了,而dev模式下我们放在了 util/dev.static.js 文件下。

根据if else可以看出,在文件 dev.static.js 文件中我们要做的事情是:把静态文件js和模版从内存中提取出来,交给app.get请求 然后 send 出去。

接下来编辑 dev.static.js 文件,首先安装 npm i axios -S

步骤一: 获取内存中的模板html文件

const axios = require(''axios'');// 在浏览器端和服务器端都可以使用 axios


/*在这里从内存中获取模版html,因为每次dev-server启动的是本地的服务,url是固定的;
这样可以根据 dev-server 实时的拿到最新的 模板文件
*/
const getTemplate = () => {
    return new Promise((resolve,reject)=>{
        axios.get(''http://localhost:8888/public/index.html'')
        .then(res => {
            resolve(res.data); //返回的内容放在了 data中
        })
        .catch(reject)
    })
}

module.exports = function (app) {
    app.get("*",function(req,res){

    })
}

步骤二:获取。server-entry.js等bundle文件

const axios = require(''axios'');
//从内存中获取 js等bundle文件,启动webpack,通过webpack打包的结果,获取bundle文件。
const webpack = require(''webpack'');
//通过 config.server.js 文件 获取 输出文件路径等信息
const serverConfig = require(''../../build/webpack.config.server.js'');

const getTemplate = () => {
    return new Promise((resolve,reject)=>{
        axios.get(''http://localhost:8888/public/index.html'')
        .then(res => {
            resolve(res.data); 
        })
        .catch(reject)
    })
}

// 通过webpack的watch方法,监听配置文件中的 entry 入口文件(及其依赖的文件)是否发生变化,一旦变化,就会重新打包(类似于热更新)
const serverCompiler = webpack(serverConfig);
serverCompiler.watch({},(err,status)=>{//status 在终端上显示的信息
    if(err) throw;
    let stats = status.toJson();
    stats.error.forEach(err => console.log(err));
    stats.waring.forEach(warn => console.warn(warn));

    const bundlePath = path.join(
        serverConfig.output.path,
        serverConfig.output.filename
    );// 获取输出文件的路径
})

module.exports = function (app) {
    app.get("*",function(req,res){

    })
}

获取到 生成的 文件名字之后需要在 内存中读取 文件:

要使用 memory-fs,所以要安装 npm i memory-fs -D;

const axios = require(''axios'');
const path = require(''path'');
const webpack = require(''webpack'');
const serverConfig = require(''../../build/webpack.config.server.js'');
// 要使用 memory-fs,所以要安装 npm i memory-fs -D;
// 在内存中读写文件,这样就可以从内存中读取 获取到的文件
const MemoryFs = require(''memory-fs'');

//最后要把得到的js文件,渲染到dom上去,所以要用到
const ReactDomServer = require(''react-dom/server'');

const getTemplate = () => {
    return new Promise((resolve,reject)=>{
        axios.get(''http://localhost:8888/public/index.html'')
        .then(res => {
            resolve(res.data); 
        })
        .catch(reject)
    })
}

//通过module的 constructor 构造方法去创建一个新的 module
const Module = module.constructior

let serverBundle;
const mfs = new MemoryFs;//new 一个 对象;
const serverCompiler = webpack(serverConfig);
serverCompiler.outputFileSystem = mfs; //webpack 提供的配置项,其输出通过mfs内存读写,这里如果写错名字就会写到硬盘中
serverCompiler.watch({},(err,status)=>{
    if(err) throw;
    let stats = status.toJson();
    stats.error.forEach(err => console.log(err));
    stats.waring.forEach(warn => console.warn(warn));

    const bundlePath = path.join(
        serverConfig.output.path,
        serverConfig.output.filename
    );// 获取输出文件的路径
    //通过 mfs 读取文件的路径,就可以得到文件,是 string 类型的文件,无法直接使用
    const bundle = mfs.readFileSync(bundlePath,''utf-8''); //需要传入 编码格式
    const m = new Module(); 
    //动态编译成一个文件,需要给这个文件指定文件名字,否则无法在缓存中进行缓存,下次则拿不到该文件
    m._compile(bundle,''server-entry.js'');//使用module的_compile方法将String的文件,生成一个新的 模块,转换成了真正可以读取的文件
    /*为了在后面的 app.get方法中使用。将生成的文件赋值给全局变量;
    此外,因为是在 watch中执行的,每次依赖的文件更新,输出的文件也会更新*/
    serverBundle = m.exports.default; 
})

module.exports = function (app) {
    app.get("*",function(req,res){
        getTemplate().then(template => {
            const content = ReactDomServer.renderToString(serverBundle);
            res.send(template.replace(''<!--app-->'',content))
        })
    })
}

 

最后在package.json 中定义命令:

{
    "script":{
        "dev:server":"cross-env NODE_ENV = development node server/sever.js"
    }

}

启动客户端和服务器端:npm run dev:client        npm run dev:server;

发现无论是js还是html都返回的一样,所以就想之前 对静态文件的 public中做的区分,但是由于这个是在内存中,所以不同:

安装:  npm i http-proxy-middleware -D   做代理的中间件

 

const axios = require(''axios'');
const path = require(''path'');
const webpack = require(''webpack'');
const serverConfig = require(''../../build/webpack.config.server.js'');
const MemoryFs = require(''memory-fs'');
const ReactDomServer = require(''react-dom/server'');
//引入中间件
const proxy = require(''http-proxy-middleware'');


const getTemplate = () => {
    return new Promise((resolve,reject)=>{
        axios.get(''http://localhost:8888/public/index.html'')
        .then(res => {
            resolve(res.data); 
        })
        .catch(reject)
    })
}

const Module = module.constructior

let serverBundle;
const mfs = new MemoryFs;
const serverCompiler = webpack(serverConfig);
serverCompiler.outputFileSystem = mfs; 
serverCompiler.watch({},(err,status)=>{
    if(err) throw;
    let stats = status.toJson();
    stats.error.forEach(err => console.log(err));
    stats.waring.forEach(warn => console.warn(warn));

    const bundlePath = path.join(
        serverConfig.output.path,
        serverConfig.output.filename
    );
    const bundle = mfs.readFileSync(bundlePath,''utf-8'');
    const m = new Module(); 
    m._compile(bundle,''server-entry.js'');
    serverBundle = m.exports.default; 
})

module.exports = function (app) {
//服务器端端口是 3333;客户端的端口是 8888;
//这里做的代理是,访问当前3333端口的public文件时,代理去请求客户端的 8888端口文件
app.use(''/public'',proxy({ target:''http://localhost:8888'' })) app.get("*",function(req,res){ getTemplate().then(template => { const content = ReactDomServer.renderToString(serverBundle); res.send(template.replace(''<!--app-->'',content)) }) }) }

 

webpack 配置react脚手架(五):mobx

webpack 配置react脚手架(五):mobx

1.  配置项。使用mobx,因为语法时es6-next,所以先配置 .babelrc 文件

{
  "presets": [
    ["es2015",{ "loose": true }],"stage-1",//改动了这里
    "react"
  ],"plugins": ["transform-decorators-legacy","react-hot-loader/babel"]
  //还有这里,transform-decorators-legacy 放在 数组的第一项
}

安装:

npm i transform-decorators-legacy babel-preset-stage-1 -D
npm i mobx-react -S

2. 使用 在store/app-state.js中:

import {
  observable,computed,autorun,action,} from ‘mobx‘

class AppState {
  @observable count = 0;
  @observable name = ‘jok‘
  @computed get msg() {
    return `${this.name} say count is ${this.count}`
  }
  @action.add(){
    this.count + =1;
  }
}
const appState = new AppState();

autorun(()=>{
  console.log(appState.msg);
})

setInterval(()=>{
  appState.add();
})
export default appState;

调用方法:

关于【webpack系列】从零搭建 webpack4+react 脚手架的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于3项目脚手架搭建npm, webpack-cli及webpack、vue+webpack4 脚手架搭建、webpack 配置react脚手架(二):热更新、webpack 配置react脚手架(五):mobx的相关知识,请在本站寻找。

本文标签: