webpack进阶篇(二十六):webpack实现SSR打包(上)

简介: webpack进阶篇(二十六):webpack实现SSR打包(上)

说明

玩转webpack学习笔记




页面打开过程

aHR0cHM6Ly9ub3RlLnlvdWRhby5jb20veXdzL3B1YmxpYy9yZXNvdXJjZS8wMmI3ODFlNGU1OWYxOTc4M2I3MmMyNmZlYmVlNzQ4MS9FQjMwNkVDRTRFRTc0M0YxODhDNDkxRjU1QTVGOUIyOQ.png



服务端渲染 (SSR) 是什么?


渲染: HTML + CSS + JS + Data -> 渲染后的 HTML


服务端:


  • 所有模板等资源都存储在服务端
  • 内⽹机器拉取数据更快
  • ⼀个 HTML 返回所有数据



浏览器和服务器交互流程


aHR0cHM6Ly9ub3RlLnlvdWRhby5jb20veXdzL3B1YmxpYy9yZXNvdXJjZS8wMmI3ODFlNGU1OWYxOTc4M2I3MmMyNmZlYmVlNzQ4MS9BMjFDNERGQ0Q2REQ0NjI5ODIzOTgxRTk4QzU0MkVDMA.png


客户端渲染 vs 服务端渲染


aHR0cHM6Ly9ub3RlLnlvdWRhby5jb20veXdzL3B1YmxpYy9yZXNvdXJjZS8wMmI3ODFlNGU1OWYxOTc4M2I3MmMyNmZlYmVlNzQ4MS9CQzBFN0I5MUMwQzY0RUUwOUZGNzdBMkQzOTg2RjM2Mg.png


总结:服务端渲染 (SSR) 的核⼼是减少请求




SSR 的优势


1、减少⽩屏时间

2、对于 SEO 友好



SSR 代码实现思路


服务端


  • 使⽤ react-dom/serverrenderToString ⽅法将
    React 组件渲染成字符串
  • 服务端路由返回对应的模板



客户端

打包出针对服务端的组件



实现 SSR


1、安装依赖

npm i express -D


2、在项目根文件添加文件夹 server,在文件夹里再添加index.js文件

if (typeof window === 'undefined') {
  global.window = {};
}
const fs = require('fs');
const path = require('path');
const express = require('express');
const { renderToString } = require('react-dom/server');
const SSR = require('../dist/search-server');
const template = fs.readFileSync(path.join(__dirname, '../dist/search.html'), 'utf-8');
const renderMarkup = (str) => {
  return template.replace('<!--HTML_PLACEHOLDER-->', str);
};
const server = (port) => {
  const app = express();
  app.use(express.static('dist'));
  app.get('/search', (req, res) => {
    console.log('SSR-----------》', SSR);
    console.log('renderToString(SSR)------>', renderToString(SSR));
    const html = renderMarkup(renderToString(SSR));
    res.status(200).send(html);
  });
  app.listen(port, () => {
    console.log(`Server is running on port:${port}`);
  });
};
server(process.env.PORT || 3000);


3、修改文件夹 search 里的 index.html,添加占位符 <!--HTML_PLACEHOLDER-->

<!DOCTYPE html>
<html lang="en">
<head>
    <%= require('raw-loader!./meta.html') %>
    <title>Document</title>
    <script><%= require('raw-loader!babel-loader!../../node_modules/lib-flexible/flexible.js') %></script>
</head>
<body>
    <div id="root"><!--HTML_PLACEHOLDER--></div>
    <script type="text/javascript" src="https://11.url.cn/now/lib/16.2.0/react.min.js"></script>
    <script type="text/javascript" src="https://11.url.cn/now/lib/16.2.0/react-dom.min.js"></script>
</body>
</html>


4、在文件夹 search 里添加 index-server.js 文件,用于服务端渲染页面

const React = require('react');
// 引入大加法
const kaimoLargeNumber = require('kaimo-large-number');
// 图片
const logo = require('./images/logo.png');
console.log(logo);
// 样式
const s = require('./search.less');
console.log(s);
class Search extends React.Component {
  constructor() {
    super(...arguments);
    this.state = {
      Text: null
    }
  }
  loadComponent() {
    // 动态加在text.js,返回一个promise
    import('./text.js').then((Text) => {
      console.log(Text);
      this.setState({
        Text: Text.default
      });
    })
  }
  render() {
    const { Text } = this.state;
    const kaimoLarge = kaimoLargeNumber('777', '666');
    return <div className="search-text">
      凯小默的博客666
      { Text ? <Text /> : null }
      大加法操作'777'+'666':{ kaimoLarge }
      <img src={ logo.default } onClick={ this.loadComponent.bind(this) } />
    </div>
  }
}
module.exports = <Search />;


5、添加 webpack.ssr.js 配置文件

const glob = require('glob');
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
const setMPA = () => {
  const entry = {};
  const htmlWebpackPlugins = [];
  const entryFiles = glob.sync(path.join(__dirname, './src/*/index-server.js'));
  Object.keys(entryFiles)
      .map((index) => {
          const entryFile = entryFiles[index];
          const match = entryFile.match(/src\/(.*)\/index-server\.js/);
          const pageName = match && match[1];
          if(pageName) {
            entry[pageName] = entryFile;
            htmlWebpackPlugins.push(
                new HtmlWebpackPlugin({
                    inlineSource: '.css$',
                    template: path.join(__dirname, `src/${pageName}/index.html`),
                    filename: `${pageName}.html`,
                    chunks: ['vendors', pageName],
                    inject: true,
                    minify: {
                        html5: true,
                        collapseWhitespace: true,
                        preserveLineBreaks: false,
                        minifyCSS: true,
                        minifyJS: true,
                        removeComments: false
                    }
                })
            );
          }
      });
  return {
      entry,
      htmlWebpackPlugins
  }
}
const { entry, htmlWebpackPlugins } = setMPA();
module.exports = {
  entry: entry,
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name]-server.js',
    libraryTarget: 'umd'
  },
  mode: 'none',
  module: {
    rules: [
      {
        test: /.js$/,
        use: [
          'babel-loader',
          'eslint-loader'
        ]
      },
      {
        test: /.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      },
      {
        test: /.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader',
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [
                require('autoprefixer')({
                  overrideBrowserslist: ['last 2 version', '>1%', 'ios 7']
                })
              ]
            }
          },
          {
            loader: 'px2rem-loader',
            options: {
              remUnit: 75,
              remPrecision: 8
            }
          }
        ]
      },
      {
        test: /.(png|jpg|gif|jpeg)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name]_[hash:8].[ext]'
            }
          }
        ]
      },
      {
        test: /.(woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name]_[hash:8].[ext]'
            }
          }
        ]
      },
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name]_[contenthash:8].css'
    }),
    new OptimizeCSSAssetsPlugin({
      assetNameRegExp: /\.css$/g,
      cssProcessor: require('cssnano')
    }),
    new CleanWebpackPlugin(),
    new webpack.optimize.ModuleConcatenationPlugin()
  ].concat(htmlWebpackPlugins),
  optimization: {
    splitChunks: {
      minSize: 0,
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'all',
          minChunks: 2
        }
      }
    }
  }
};



6、往 package.json 里添加脚本

{
  "scripts": {
    "build:ssr": "webpack --config webpack.ssr.js"
  }
}



7、运行 npm run build:ssr,打包成功之后,然后再 node server/index.js

8、打开浏览器访问 http://localhost:3000/search, 就可以看到下面效果

aHR0cHM6Ly9ub3RlLnlvdWRhby5jb20veXdzL3B1YmxpYy9yZXNvdXJjZS8wMmI3ODFlNGU1OWYxOTc4M2I3MmMyNmZlYmVlNzQ4MS84MDg4NTgyN0ExMkU0RUNGQkY5MkJDMjJERjFGRjc1Qg.png

目录
相关文章
|
4月前
|
缓存 前端开发 JavaScript
Webpack 打包的基本原理
【10月更文挑战第5天】
|
4月前
|
前端开发 JavaScript
ES6模块化和webpack打包
【10月更文挑战第5天】
|
5月前
|
JavaScript 测试技术 Windows
vue配置webpack生产环境.env.production、测试环境.env.development(配置不同环境的打包访问地址)
本文介绍了如何使用vue-cli和webpack为Vue项目配置不同的生产和测试环境,包括修改`package.json`脚本、使用`cross-env`处理环境变量、创建不同环境的`.env`文件,并在`webpack.prod.conf.js`中使用`DefinePlugin`来应用这些环境变量。
369 2
vue配置webpack生产环境.env.production、测试环境.env.development(配置不同环境的打包访问地址)
|
4月前
|
缓存 前端开发 JavaScript
深入了解Webpack:模块打包的革命
【10月更文挑战第11天】深入了解Webpack:模块打包的革命
|
4月前
|
缓存 前端开发 JavaScript
Webpack技术深度解析:模块打包与性能优化
【10月更文挑战第13天】Webpack技术深度解析:模块打包与性能优化
|
4月前
|
前端开发 JavaScript 开发者
深入了解Webpack:现代JavaScript应用的打包利器
【10月更文挑战第11天】 深入了解Webpack:现代JavaScript应用的打包利器
|
5月前
|
JavaScript 前端开发
手写一个简易bundler打包工具带你了解Webpack原理
该文章通过手写一个简易的打包工具bundler,帮助读者理解Webpack的工作原理,包括模块解析、依赖关系构建、转换源代码以及生成最终输出文件的整个流程。
|
5月前
|
JavaScript
webpack打包TS
webpack打包TS
147 60
|
5月前
|
缓存
webpack 打包多页面应用
webpack 打包多页面应用
58 1
|
5月前
|
前端开发 开发者
在前端开发中,webpack 作为一个强大的模块打包工具,为我们提供了丰富的功能和扩展性
【9月更文挑战第1天】在前端开发中,Webpack 作为强大的模块打包工具,提供了丰富的功能和扩展性。本文重点介绍 DefinePlugin 插件,详细探讨其原理、功能及实际应用。DefinePlugin 可在编译过程中动态定义全局变量,适用于环境变量配置、动态加载资源、接口地址配置等场景,有助于提升代码质量和开发效率。通过具体配置示例和注意事项,帮助开发者更好地利用此插件优化项目。
104 13