欢迎访问企业官网建站公司网站,拥有丰富的建站经验,快速智能且精美!

企业官网建站公司

自助建站官网平台建设,公司自助官网建站定制

VuePress源码阅读(四) -- 「成"站"之日」markdown建站实战篇

作者:jcmp      发布时间:2021-05-03      浏览量:0
系列文章:既然在上篇文章完成了对 mar

系列文章:

既然在上篇文章完成了对 markdown建站的分析,那么肯定要来个实战了,话不多说马上开始.

一、用 markdown 文件实现最简建站

这次实战的目标是使用一个 README.md 文件通过 Webpack 构建网站,支持简单的预览和打包.

mkdir vuepress-studycd vuepress-studymkdir docsecho '# Hello VuePress' > docs/README.mdtouch webpack.config.js# 安装依赖yarn init -yyarn add -D webpack webpack-cli markdown-it html-webpack-plugin clean-webpack-plugin html-loader。

这里先通过 webpack.config.js 配置来编写,之后再用 webpack-chain 实现一遍.

使用 markdown-it 的 demo 作为 README.md 的内容方便测试 markdown-it 的效果,链接为: https://markdown-it.github.io/

由于现在只有一个 markdown 文件(docs/README.md),所以作为 Webpack 入口的 js 和 markdown-loader 都需要由我们自行提供:

mkdir pkgmkdir pkg/clienttouch pkg/client/clientEntry.jsmkdir pkg/markdown-loadertouch pkg/markdown-loader/index.js。

将 Webpack 官网例子改成引入 README.md 就形成了入口文件 clientEntry.js ,目标是网站能够将 markdown 转换结果添加到页面上:

import mdHTML from '../../docs/README.md'function component() { var element = document.createElement('div'); console.log(typeof mdHTML) element.innerHTML = mdHTML return element;}document.body.appendChild(component());

接着简单地引入 markdown-it 编写出 markdown-loader:

'use strict'const md = require('markdown-it')module.exports = function (content) { const markdown = md() const html= markdown.render(content) return html}

最后编写 webpack.config.js 如下:

const path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');const { CleanWebpackPlugin } = require('clean-webpack-plugin');module.exports = { mode: 'development', entry: path.resolve(__dirname, 'pkg/client/clientEntry.js'), output: { filename: 'app.js', path: path.resolve(__dirname, 'dist'), }, module: { rules: [ { test: /\.md$/, use: [ { loader: 'html-loader', }, { // 使用项目中自带的 loader loader: require.resolve('./pkg/loader/markdown-loader'), } ], }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin(), ],}

执行打包命令 npx webpack 完成打包:

用命令行查看打包结果,并使用 http-server 运行本地服务器:

OK,除了一些 emoji 没显示图像,大部分布局基本一致.

这部分代码已经放到 Github 上了有兴趣可以看看:

二、整合 Vue 构建网站

1. 使用 Webpack + Vue 构建网站

上面只是个纯静态的最简网站搭建,而 VuePress 是基于 Vue 构建的,那么现在我们也来模拟这个流程.

首先是跑起在当前项目加入 vue,然后创建Vue实例并完成相应打包.

所以修改入口文件 pkg/client/clientEntry.js 为:

import { createApp } from './app'const { app } = createApp()app.$mount('#app')。

其中的 createApp 工厂函数就是用来创建新的 Vue 实例的,所以编写 pkg/client/app.js 如下:

import Vue from 'vue'import App from './App.vue'// 导出一个工厂函数,用于创建新实例export function createApp () { const app = new Vue({ // 根实例简单的渲染应用程序组件。 render: h => h(App) }) return { app }}

创建 App.vue 如下:

并且准备好 index.html 以供 html-webpack-plugin 使用:

接着安装对应依赖,其中 vue-loader 和 vue-template-compiler 需要一起安装,保证处于同个版本:

yarn add vueyarn add -D vue-loader vue-template-compiler。

最后修改 webpack.config.js 如下:

接着运行 npx webpack 完成打包,可以看到页面已经成功显示出 App.vue 的内容:

这部分代码已经放到 Github 上了有兴趣可以看看:

2. 将 markdown 编译结果插入 Vue 网站

上面我们已经将 Vue 整合到项目中并成功地完成了构建,但是由于入口文件 pkg/client/clientEntry.js 的逻辑变化导致网站并没有引用 markdown 的打包结果.

因为现在还没有加入 vue-router 来做路由管理,所以暂时使用比较原始的方法做过渡 -- 直接在 App.vue 做引入(由于直接返回字符串所以用 v-html 实现展示):

效果如下:

OK,至此我们已经使用 markdown 文件成功地创建了一个Vue 驱动的 SPA 网站,并且实现客户端渲染(CSR)方式的打包.

三. 实现服务端渲染(SSR)方式打包

回忆一下前几篇文章里面 VuePress 打包都是构建为 SSR 形式的客户端激活程序+服务端渲染程序,再运行服务端渲染程序生成SSR HTML,这里我们也来实践一下.

首先完成依赖的安装:

yarn add vue vue-server-rendereryarn add -D webpack-node-externals。

构建 SSR 的客户端激活部分的入口文件依旧使用 client/clientEntry.js 即可,而服务端渲染程序则需要替换为 client/serverEntry.js:

import { createApp } from './app'export default context => { const { app } = createApp() return app}

由于CSR 和 SSR 的配置并不相同,所以这里将公共配置提取到 webpack.base.config.js ,配置文件 webpack.ssrclient.config.js 和 webpack.ssrserver.config.js 合并公共配置,然后用来处理 SSR 打包,,以此来生成 client.json 和 server.json.

1. 提取公共配置文件

webpack.base.config.js 的配置如下,主要是提取了 loader 和 VueLoaderPlugin:

const VueLoaderPlugin = require('vue-loader/lib/plugin')module.exports = { module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', }, { test: /\.md$/, use: [ { loader: 'html-loader', }, { // 使用项目中自带的 loader loader: require.resolve('./pkg/loader/markdown-loader'), } ], }, ], }, plugins: [ new VueLoaderPlugin(), ]}

2. SSR客户端程序打包

webpack.ssrclient.config.js 的配置如下,这是参考 Vue SSR -- 构建配置-客户端配置 写的,但是由于 webpack4 后期已经取消掉 webpack.optimize.CommonsChunkPlugin,所以这里我使用 splitChunks 来替代:

const merge = require('webpack-merge')const baseConfig = require('./webpack.base.config.js')const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')module.exports = merge(baseConfig, { mode: 'production', entry: './pkg/client/clientEntry.js', plugins: [ // new webpack.optimize.CommonsChunkPlugin({ // name: "manifest", // minChunks: Infinity // }), // 此插件在输出目录中生成 `vue-ssr-client-manifest.json`。 new VueSSRClientPlugin() ], optimization: { splitChunks: { name: 'manifest', minChunks: Infinity } },})。

运行 npx webpack --config webpack.ssrclient.config.js 进行打包:

结果生成了 main.js 和对应的 vue-ssr-client-manifest.json :

3.SSR服务端程序打包

webpack.ssrserver.js 的配置如下:

const merge = require('webpack-merge')const nodeExternals = require('webpack-node-externals')const baseConfig = require('./webpack.base.config.js')const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')module.exports = merge(baseConfig, { mode: 'production', entry: './pkg/client/serverEntry.js', target: 'node', devtool: 'source-map', output: { libraryTarget: 'commonjs2', }, externals: nodeExternals({ allowlist: /\.css$/ }), // 这是将服务器的整个输出 // 构建为单个 JSON 文件的插件。 // 默认文件名为 `vue-ssr-server-bundle.json` plugins: [ new VueSSRServerPlugin() ]})。

第一次运行的结果:

检查我的配置编写应该没有问题之后,想起 VuePress 用的 Webpack4,而我用的是 Webpack5 所以可能会出一些问题,所以网上搜索了一下找到了解决方法 https://github.com/vuejs/vue/issues/11718 ,为了方便大家查看我就把重点截一下图:

刚好一个人问了这个问题然后上面这个老哥回复了解决方案,需要修改 node_modules/vue-server-renderer/server-plugin.js 的代码,当然这不是太工程化的处理方案,但现在暂时也只能这样解决了~(提出解决方案的老哥也提出 Webpack4 现在暂时是得到更广泛的支持的,如果想少遇点问题可以暂时切回 Webpack4).

修改之后的运行结果:

现在打包结果目录的情况如下:

4. 执行服务端渲染

上面我们已经成功获取到了两个json 文件,那么现在我们来编写服务端渲染程序,生成一个SSR HTML.

创建 pkg/client/index.ssr.html 如下:

{{ title }}

然后编写渲染程序 pkg/client/renderSSRHTML.js:

const fs = require('fs')const path = require('path')const { createBundleRenderer } = require('vue-server-renderer')const serverBundle = require(path.resolve('../../dist/vue-ssr-server-bundle.json'))const clientManifest = require(path.resolve('../../dist/vue-ssr-client-manifest.json'))const ssrHTML = ` {{ title }} `async function main () { // create server renderer using built manifests const renderer = createBundleRenderer(serverBundle, { clientManifest, runInNewContext: false, inject: false, shouldPrefetch: () => true, template: ssrHTML }) const context = { title: 'VuePress', lang: 'en', version: '1.0' } let html = await renderer.renderToString(context) fs.writeFile('../../dist/index.html', html, () => { console.log('finish') })}main()。

执行服务端渲染程序:

生成 index.html 成功:

来看看效果,控制台中又看到了熟悉的 data-server-rendered 了~

至此服务端渲染(SSR)方式打包渲染的方式已经基本实现,markdown经过层层处理终于成为一个网站了~

这部分代码已经放到 Github 上了有兴趣可以看看: