webpack 配置react脚手架

1 react 文件基本:

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

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

// 把app组件传给 reactCreateElement 作为的参数 要引入 react

基本配置:

const path = require('path');
module.exports = {
    entry:[
        app: path.join(__dirname,'../clinet/app.js'); //使用path,设置绝对路径,注意 dirname前面是两个下划线
    ],
    output:[
        filename:'[name].[hash].js', //依赖的文件发生变化的时候,hash值变化。
        path:path.join(__dirname,'../dist'),//输出的绝对路径
        publicPath:'/public' //设置引用文件的公共路径,用于设置CDN的路径
    ],
    module:{
        rules:[
            {
                test:/.jsx$/,
                loader:'babel-loader'//编译jsx,es6,es7等语法
                //这里要安装 babel: npm i babel-loader -D
                //babel-loader只是一个webpack的插件,不包括babel的核心代码,所以还要安装:
                //babel-core: npm i bable-core -D
                //此外,bable-core 默认是编译 es6的,为了还编译jsx,所以要进行另外的配置:.babelrc 文件
            }
        ]
    }
}
// .babelrc 文件
{
    "presets":[ //指定babel编译哪些类型
        ["es2015",{"loose":true}],//指定 es2015是 松散的不是严格的
        "react"    //在这里加上react,这个支持之后,babel才会编译react代码
    ]
}
//安装上述依赖文件:
// npm i babel-preset-es2015 babel-preset-es2015-loose babel-preset-react -D

为了打开html文件:

安装: 1 npm i html-webpack-plugin -D 

const path = require('path');
const HTMLPlugin =require('html-webpack-plugin')
module.exports = {
    entry:[
        app: path.join(__dirname,'../clinet/app.js'); //使用path,设置绝对路径,注意 dirname前面是两个下划线
    ],
    output:[
        filename:'[name].[hash].js', //依赖的文件发生变化的时候,hash值变化。
        path:path.join(__dirname,'../dist'),//输出的绝对路径
        publicPath:'/public' //设置引用文件的公共路径,用于设置CDN的路径
    ],
    module:{
        rules:[
            {
                test:/.jsx$/,
                loader:'babel-loader'//编译jsx,es6,es7等语法
                //这里要安装 babel: npm i babel-loader -D
                //babel-loader只是一个webpack的插件,不包括babel的核心代码,所以还要安装:
                //babel-core: npm i bable-core -D
                //此外,bable-core 默认是编译 es6的,为了还编译jsx,所以要进行另外的配置:.babelrc 文件
            },
            {
                test:/.js$/,
                loader:'babel-loader',
                exclude:[
                    path.join(__dirname,'../node_modules')
                ]
            }
        ]
    },
    plugins:[
        new HTMLPlugin(); //有两个作用:1 生成html文件 ,2:根据output的配置把entry 文件打包
    ]
}

2 服务端渲染的配置

单页面存在的问题:

1 SEO不友好

2 首次请求等待时间过长,体验不友好。

页面代码:

// 在服务端运行的文件
import React from 'react';
import App from './App.jsx';

export default <App/>

新建一个服务器端的webpack打包的文件:

const path = require('path');
module.exports = {
    target:'node', //这里是新的配置,web/node 规定在哪里使用
    entry:[
        app: path.join(__dirname,'../clinet/serve-entry.js'); 
    ],
    output:[
        filename:'server-entry.js', //node.js 去 improt 这个js文件,且在服务器端没有缓存,不需要hash
        path:path.join(__dirname,'../dist'),//输出的绝对路径
        publicPath:'',
        libraryTarget: 'commonjs2' //打包出来的js 使用的模块方案, 包括 AMD CMD CommonJS等规范,这里使用的是commonjs2,适用于node端
    ],
    module:{
        rules:[
            {
                test:/.jsx$/,
                loader:'babel-loader'//编译jsx,es6,es7等语法
                //这里要安装 babel: npm i babel-loader -D
                //babel-loader只是一个webpack的插件,不包括babel的核心代码,所以还要安装:
                //babel-core: npm i bable-core -D
                //此外,bable-core 默认是编译 es6的,为了还编译jsx,所以要进行另外的配置:.babelrc 文件
            },
            {
                test:/.js$/,
                loader:'babel-loader',
                exclude:[
                    path.join(__dirname,'../node_modules')
                ]
            }
        ]
    }
}

修改配置项:

//修改 package.json文件

{
    "script":{
        "build:clinet": "webpack --config build/webpack.config.client.js",
        "build:server": "webpack --config build/webpack.config.server.js",
        "clear": "rimraf dist" //rimraf 是node的一个包,专门用来删除文件夹
        "build": "npm run clear && npm run build:client && npm run build:server",        
    }
}

//安装rimraf包。 npm i rimraf -D 

然后每次打包完,会有三个文件 客户端的js 服务器端的js 还有一个html。

下面编写 express 服务端:首先安装  npm i express -S 

新建 server文件夹 建立 server.js文件:

说明: 1 :dist/server-entry文件是打包生成的js文件;res.send()渲染的就是生成的js文件;

2.app.get 获取到浏览器所有的请求,返回 渲染的 js文件;监听端口在3333

2: 由于html中 使用的是es6的语法: export default <App/>;而node中又不能使用 impront { app } from './app' 这种结构赋值的语法,所以这里要用 require().default 获取默认值;

然后在配置json文件中,设置服务端启动命令:

{
    "script":{
        "start": "node server/server.js    "    
    }
}

 运行服务端代码之后,服务端返回的只是打包后入口文件中写的代码 字符串:‘<div></div>’,我们需要返回的是整个html。

 步骤一:先在client文件夹下新建 模板文件 template.html: 其中关键代码:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="root"><app></app></div>
</body>
</html>

把生成的文件覆盖掉 <app></app>;

然后修改客户端的配置文件。webpack.config.client.js中的模板插件:

{
    plugins: [
        new HTMLPlugin({
          template: path.join(__dirname, '../client/template.html')
        })
      ]
}

注意这里是改动的客户端的配置文件,客户端打包生成的html文件 ,然后服务端去引用:

const express = require('express')
const ReactSSR = require('react-dom/server');
const fs = require('fs')
const path = require('path')
const serverEntry = require('../dist/server-entry').default;//引入的是服务端的配置打包后的js文件
const template = fs.readFileSync(path.join(__dirname, '../dist/index.html'), 'utf8')//同步引入客户端打包生成的 html 文件,如果不使用 utf8 则是buffer文件

const app = express();
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></app>',appString)) //用返回的js文件替换掉模板中的<app>,然后发送的是模板文件
})

app.listen(3333, function () {
  console.log('server is listening on 3333')
})

注意的是: 客户端和服务端的 output配置中的 publicPath 要写为“public”

output:[
        filename:'[name].[hash].js', //依赖的文件发生变化的时候,hash值变化。
        path:path.join(__dirname,'../dist'),//输出的绝对路径
        publicPath:'/public' //设置引用文件的公共路径,用于设置CDN的路径
]

这样生成的html中引用的静态文件js和css以及图片等路径前缀都是在public下,所以 上面的服务端 server.js 的  app.use('/public', express.static(path.join(__dirname, '../dist')));  

给访问的 public 目录下的文件返回 dist文件夹下的文件,其他的 * 返回 模板文件

  • 问题1 为啥还要在 服务端的配置文件中 设置 public
  • 问题2 服务端的 server-entry.js 和 客户端的 app.js 有啥区别?


客户端的app.js可以对浏览器进行操作,比如

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

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


而服务器端不可以:

import React from 'react';
import App from './App.jsx';

exprot defalut <App/>


所以后续,对应的webpack.config配置文件也需要设置两个,分别对应客户端和服务器端;
他们两个的入口和出口配置不一样
客户端的配置文件:

{
     entry:[
        app: path.join(__dirname,'../clinet/app.js'); //使用path,设置绝对路径,注意 dirname前面是两个下划线
    ],
    output:[
        filename:'[name].[hash].js', //依赖的文件发生变化的时候,hash值变化。
        path:path.join(__dirname,'../dist'),//输出的绝对路径
        publicPath:'/public' //设置引用文件的公共路径,用于设置CDN的路径
    ],
}


服务器端的配置文件:

{
    target:'node', //这里是新的配置,web/node 规定在哪里使用
    entry:[
        app: path.join(__dirname,'../clinet/serve-entry.js');
    ],
    output:[
        filename:'server-entry.js', //node.js 去 improt 这个js文件,且在服务器端没有缓存,不需要hash
        path:path.join(__dirname,'../dist'),//输出的绝对路径
        publicPath:'',
        libraryTarget: 'commonjs2' //打包出来的js 使用的模块方案, 包括 AMD CMD CommonJS等规范,这里使用的是commonjs2,适用于node端
    ],
}


因此,服务器端是单独的入口js文件和输出文件。
根据模板文件生成:

<body>
    <div id="root"><!-- app --></div>
    <script type="text/javascript" src="/public/app.9337b7bd3bfeb9af5f86.js"></script>
</body>

其中客户端打包,将客户端的js文件放在了script标签下,服务器端的 server-entry.js文件在服务器端的 server.js 放在了 id == 'root' 的 div中,所以两个都要加 /public,这样服务器就会返回对应的js文件,而不是 模板代码。




猜你喜欢

转载自www.cnblogs.com/xiaozhumaopao/p/10991630.html