vue-ssr(上)

vue-ssr解决什么问题?

  • spa全部靠的是js来渲染,默认首页显示内容是一个空的div标签,不利于SEO搜索引擎搜索

  • 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的

  • spa应用会有首页白屏时间过长的问题,服务器渲染的好处是将访问好的数据拼接好给前端,首页白屏时间缩短

服务端渲染的缺点?

需要占用服务器的CPU和内存,前端中的一些生命周期函数无法使用

在这里插入图片描述

vue-ssr使用

使用的包

vue 
vue-server-renderer 
koa
koa-router

示例

template.html: 这里的 <!--vue-ssr-outlet--> 是固定字段

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!--vue-ssr-outlet-->
</body>

</html>
const Koa = require('koa');
const Router = require('koa-router');
const Vue = require('vue');
const VueServerRenderer = require('vue-server-renderer');
const app = new Koa();
const router = new Router();
const path = require('path');
const fs = require('fs');

const vm = new Vue({
    data: {
        name: '张三'
    },
    template: '<div>hello {{name}}</div>'
})

const template = fs.readFileSync(path.resolve(__dirname, 'template.html'), 'utf8');

router.get('/', async (ctx) => {
    ctx.body = await VueServerRenderer.createRenderer(template).renderToString(vm);
})
//使用路由
app.use(router.routes());
app.listen(3000, () => {
    console.log('start server success')
})

通过webpack实现编译vue项目

依赖包
npm i webpack webpack-cli webpack-merge -D
npm i @babel/core @babel/preset-env babel-loader -D
npm i vue-loader vue-style-loader vue-template-compiler -D
npm i css-loader html-webpack-plugin concurrently -D 
项目结构
ssr
├── dist
│   ├── client.bundle.js
│   ├── client.html
│   ├── server.bundle.js
│   └── server.html
├── package-lock.json
├── package.json
├── public
│   ├── index.html
│   └── index.ssr.html
├── server.js
├── src
│   ├── App.vue
│   ├── client-entry.js
│   ├── components
│   │   ├── Bar.vue
│   │   └── Foo.vue
│   ├── main.js
│   └── server-entry.js
├── vue-ssr.md
├── webpack.base.js
├── webpack.client.js
└── webpack.server.js
公共webpack配置
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
    mode: 'development',
    output: '[name].bundle.js',
    module: {
        rules: [{
                test: /\.css$/,
                use: ['vue-style-loader', {
                    loader: 'css-loader',
                    options: {
                        esModule: false, //注意配合vue-style-loader使用时需要加上这个属性
                    }
                }]
            },
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            },
            {
                test: /\.vue$/,
                use: 'vue-loader'
            }
        ]
    },
    plugins: [
        new VueLoaderPlugin() //
    ]
}
客户端webpack配置
const path = require('path');
cosnt HtmlWebpackPlugin = require('html-webpack-plugin');
const {
    merge
} = reqire('webpack-merge');
const base = require('./webpack.base.js');
module.exports = (base, {
    entry: {
        client: path.resolve(__dirname, './src/client-entry.js')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, './public/index.html'),
            filename: 'client.html'
        })
    ]
})
服务端webpack配置
const path = require('path');
cosnt HtmlWebpackPlugin = require('html-webpack-plugin');
const {
    merge
} = reqire('webpack-merge');
const base = require('./webpack.base.js');
module.exports = merge(base, {
    target: 'node', //目标是给node使用
    entry: {
        server: path.resolve(__dirname, './src/server-entry.js')
    },
    output: {
        libraryTarget: 'commonjs2' //让打包后的server.bundle.js用module.exports导出
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, './public/index.ssr.html'),
            filename: 'server.html',
            excludeChunks: ['server'], //打包后server.html中不引入server.bundle.js
            mimyfy: false, //不压缩,防止<!--vue-ssr-outlet-->被覆盖
        })
    ]
})
index.ssr.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!--vue-ssr-outlet-->
    <script src="./client.bundle.js"></script>
</body>

</html>
index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app"></div>
</body>

</html>
main.js
const Vue from 'vue';
const App from './App.vue';

export default () => {
    const app = new Vue({
        render: h => h(App)
    })
    return {
        app
    }
}
client-entry.js
const createApp from './main.js';
const {
    app
} = createApp();
app.$mount('#app');
server-entry.js
const createApp from './main.js';
export default () => {
    const {
        app
    } = createApp();
    return app;
}
server.js
const Koa = require('koa');
const Router = require('koa-router');
const static = require('koa-static');
const VueServerRenderer = require('vue-server-renderer');
const fs = require('fs');
const path = require('path');

const app = new Koa();
cont router = new Router();

const serverBundle = fs.readFileSync(path.resolve(__dirname, './dist/server.bundle.js'));
const template = fs.readFileSync(path.resolve(__dirname, './dist/server.html'));
const render = VueServerRenderer.createBundleRenderer(serverBundle, {
    template
})

router.get('/', async (ctx) => {
    ctx.body = new Promise((resolve, reject) => {
        render.renderToString((err, html) => {
            if (err) reject(err);
            resolve(html);
        })
    })
})

app.use(route.routes())
app.use(static(path.resolve(__dirname, './dist')));
app.listen(3000, () => {
    console.log('start server success');
})
App.vue

这里需要注意的是,在最外层需要添加一个 id='app' , ssr官网上有说 客户端激活 , data-server-rendered 特殊属性,让客户端 Vue 知道这部分 HTML 是由 Vue 在服务端渲染的,并且应该以激活模式进行挂载。注意,这里并没有添加 id=“app”,而是添加 data-server-rendered 属性:你需要自行添加 ID 或其他能够选取到应用程序根元素的选择器,否则应用程序将无法正常激活,

<template>
  <div id='app'>
    <Foo></Foo>
    <Bar></Bar>
  </div>
</template>

<script>
import Bar from "./components/Bar.vue";
import Foo from "./components/Foo.vue";
export default {
  components: {
    Foo,
    Bar,
  },
};
</script>
Bar.vue
<template>
    <div>bar</div>
</template>

<style scoped="true">
div{
    background:red
}
</style>
Foo.vue
<template>
    <div @click="show">foo</div>
</template>
<script>
export default {
    methods:{
        show(){
            alert(1)
        }
    }
}
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值