1 react 基本js文件:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx'; ReactDOM.render(<App/>,document.getElementById('root')) // 把app组件传给 reactCreateElement 作为的参数 要引入 react

基本 webpack.config 配置:

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文件

// .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

webpack.config.js 文件

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 首次请求等待时间过长,体验不友好。

首先看一下客户端的入口文件 app.js

客户端的入口文件

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx'; ReactDOM.render(<App/>,document.getElementById('root'))

对比服务端渲染的入口文件  server-entry.js 文件:都是为了引入 最外层组件 <App/> 但是服务端没有dom元素,所以只能抛出 <App/> 组件,而不能挂载到 root的div节点上。

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

服务端和客户端的入口文件都是把<App> 组件提供出来,不同的是客户端可以直接把组件挂载到 root上。而服务器端的入口文件是提供 <App/>组件,之后在服务器端的server.js中,将其挂载到root中;且客户端和服务器端用的是同一个html模板。因为入口文件不同,所以两个的webpack配置文件也不同:

新建一个服务器端的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:'/public/',
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。

上面介绍的是 客户端和服务器端的 webpack 配置文件;

===================

下面编写 express 服务端:

首先安装  npm i express -S

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

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

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

3. 由于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--></div>
</body>
</html>

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

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

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

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

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-->',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'))

而服务器端的入口文件 server-entry 不可以:

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:'/public/',
libraryTarget: 'commonjs2' //打包出来的js 使用的模块方案, 包括 AMD CMD CommonJS等规范,这里使用的是commonjs2,适用于node端
],
}

因此,服务器端和客户端是单独的入口js文件和输出文件。

客户端: client/app.js------> dist/app.hash.js 文件,如下图所示,其提供的组件<App/>挂载到 root下;

服务器端: client/server-entry.js------> dist/server.entry.js 文件,在服务器端 server/server.js的作用下,将其生成的<App/>组件挂载到 root 下。

也就是说,客户端的app.js文件,生成的组件挂载到 root上;服务器端把server-entry生成的组件直接挂载到 root 上。
根据模板文件生成:

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

两个是独立的,所以两个都要加 /public,这样服务器就会根据两个位于 public 文件夹下 而返回对应的app.hash.js 和 server.entry.js文件,而不是 模板代码。

webpack 配置react脚手架的更多相关文章

  1. webpack 配置react脚手架(六):api

    1 访问网址 https://cnodejs.org/api 可以调取api 2.//该body-parser 可以将请求的body数据,转变成 json 格式数据://express-session ...

  2. webpack 配置react脚手架(三):eslint 及优化

    首先谨记 eslint的官网:  http://eslint.cn/ 1 安装eslint  npm i eslint -D 2.在根目录下新建文件 .eslintrc { "extends ...

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

    下面继续配置 webpack dev server    hot module replacement: 首先配置dev-server     安装     npm i webpack-dev-ser ...

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

    1.  配置项.使用mobx,因为语法时es6-next,所以先配置 .babelrc 文件 { "presets": [ ["es2015", { " ...

  5. webpack 配置react脚手架(四):路由配置

    1. 由于 react-router 是集成了 react-router-dom 和 react-router-native的一起的,所以这里要使用的是 react-router-dom, 2. 安装 ...

  6. [webpack] 配置react+es6开发环境

    写在前面 每次开新项目都要重新安装需要的包,简单记录一下. 以下仅包含最简单的功能: 编译react 编译es6 打包src中入口文件index.js至dist webpack配置react+es6开 ...

  7. webpack配置React开发环境(上)

    Webpack 是一个前端资源加载/打包工具,我们部门的一条主要技术栈就是Webpack+React+ES6+node,虽然之前自己做个人项目也接触好多次Webpack,但是自己并没有研读总结过Web ...

  8. 使用webpack配置react并添加到flask应用

    学习react,配置是很痛苦的一关,虽然现在有了create-react-app这样方便的工具,但是必须要自己配置一遍,才能更好地进行项目开发. 首先要明确一个概念:react的文件必须经过编译才能被 ...

  9. 基于webpack的react脚手架

    一.前言:react的cli开发模式太过于简单,好多东西都要自己配置 二.这里有个简单的配置,可以直接上手开发(不熟悉webpack和npm的绕路),已经完成的配置如下 1:默认ejs模板 2:编译l ...

随机推荐

  1. 最新 阿里java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.阿里等10家互联网公司的校招Offer,因为某些自身原因最终选择了阿里.6.7月主要是做系统复习.项目复盘.LeetCode ...

  2. nginx之配置proxy_set_header问题梳理

    客户端请求web服务,客户端:ip:192.168.223.1 nginx作为反向代理服务器:192.168.223.136 nginx作为后端web服务器:192.168.223.137 前提条件: ...

  3. 第6章:LeetCode--数组(冒泡排序、快速排序)

    11. Container With Most Water class Solution { public: int maxArea(vector<int>& height) { ...

  4. k8s集群监控 cadvisor/exporter+prometheus+grafana

    ### k8s监控处理 ### 1.cadvisor/exporter+prometheus+grafana 安装#### 1.1 配置nfs安装```shellubuntu: nfs 服务器 apt ...

  5. WUST 设计模式 实验九 观察者模式的应用

    实验九 观察者模式的应用 一.实验目的 掌握外观模式(Observer)的特点: 分析具体问题,使用外观模式进行设计. 二.实验内容和要求   网上商店中如果商品(product)在名称(name). ...

  6. pandas之数据IO笔记

    pandas在进行数据存储与输出时会做一些相应的操作 1.*索引:将一个列或多个列读取出来构成DataFrame,其中涉及是否从文件中读取索引以及列名 2 *类型推断和数据转换:包括用户自定义的转换以 ...

  7. 在一个form表单中实现多个submit不同的action

    在button中用JS的事件绑定onclick实现,如下: <!-- employees是表单的name属性值--> <script type="text/javascri ...

  8. Nginx 路由重写

    很多时候我们的真实路由是隐藏的,都经过重写后展现到前台,下面简单写两个我经常用到的几个: 一般在配置*.host(在http里面引入的server配置)的时候会用到每个不同网址的路由重写,每一个rew ...

  9. (一)SpringMvc简介以及第一个springmvc工程

    一.SpringMVC是什么? springmvc是Spring的一个模块,提供web层解决方案(就与MVC设计架构) 如上图, DispatcherServlet:前端控制器,由SpringMVC提 ...

  10. AngularJS视图 ng-view

    AngularJS支持通过在单个页面上的多个视图的单页应用.要做到这一点AngularJS提供ng-view 和 ng-template指令,以及 $routeProvider 服务. ng-view ...