easywebpack + egg + vue

简介

Egg + Vue工程化解决方案

参考链接:Egg + Vue 工程化解决方案

安装

方式一:easywebpack-cli 脚手架初始化项目

  • 安装脚手架:npm i easywebpack-cli -g
  • 测试是否安装成功:easywebpack -V或者easy -V
  • 初始化:easywebpack init
  • 安装依赖:npm i

备注:用了几次都没有成功初始化

方式二:Github 仓库代码初始化项目

  • git clone https://github.com/hubcarl/egg-vue-webpack-boilerplate.git
  • 安装依赖:npm i

方式三:vscode 插件初始化项目

  • 打开vscode
  • 点击左侧插件按钮
  • 搜索插件:vscode-easy-plugin
  • 安装插件
  • 使用插件初始化

项目介绍

  • 目录结构基本上跟egg.js一样,遵循egg.js规范。参考:egg入门笔记
  • 前端工程目录:app/web/目录
  • 页面入口目录:app/web/page/目录

渲染模式

egg-view-vue-ssr

目前egg-view-vue-ssr支持服务端渲染模式前端渲染模式两种渲染模式

服务端渲染

  • 编写的Vue组件在Node服务端直接编译成HTML,然后直接输入给浏览器
  • 使用render方法或者renderToHtml方法实现服务器渲染
  • 与客户端渲染相比:支持SEO、更快的首屏渲染
  • 与传统的模板引擎相比:更好的组件化、前后端模板共用
  • 数据驱动,更快的开发效率
  • 适用于:有一定交互性,对SEO、首屏速度有要求的业务
// controller/home.js module.exports = app => { return class HomeController extends app.Controller { async index() { const { ctx } = this; await ctx.render('home/home.js', Model.getPage(1, 10)); } async index2() { const { ctx } = this; const html = await ctx.renderToHtml('home/home.js', Model.getPage(1, 10)); // 这里可以处理对渲染后的 HTML 进行处理 ctx.body = html; } }; };

客户端渲染

  • Node端只会根据htmlheadbody节点信息的layout文件输出骨架内容,页面的实际内容交给浏览器去渲染
  • 使用renderClient方法实现客户端渲染
  • 使用客户端渲染时,需要有layout骨架文件
  • 默认renderClient会对layout HTML模板进行Vue编译;如果不想对layout HTML进行Vue编译,可以在第三个参数中传入{ viewEngine: null }
  • 使用renderClient的第三个扩展参数配置layout,优先级高于全局layout配置
const Model = require('../../mocks/article/list'); module.exports = app => { return class HomeController extends app.Controller { async client() { const { ctx } = this; const locals = { seo: { keyword: 'Egg,Vue,SSR', description: 'Egg Vue SSR Development'}, data: Model.getPage(1, 10) }; // 如果不想对 layout HTML 进行 Vue 编译,传入第三个参数 { viewEngine: null } // await ctx.renderClient('home/home.js', locals, { viewEngine: null }); await ctx.renderClient('home/home.js', locals); } async client2() { const { ctx } = this; // 个性化配置 layout 文件 await ctx.renderClient('home/home.js', {}, { layout: path.join(app.baseDir, 'app/web/page/home/layout.html') }); } }; };

egg-view-vue-ssr在项目中的使用

  • 添加插件在config/plugin.js文件中
exports.vuessr = { enable: true, package: 'egg-view-vue-ssr' };
  • 插件配置在config/default.js文件中
// ... module.exports = app => { // ... exports.vuessr = { // layout 文件位置。当使用 renderClient 实现客户端渲染时需要这个 layout 文件 // 这只是一个简单的骨架文件,具体内容由前端进行渲染 layout: path.join(app.baseDir, 'app/web/view/layout.html'), renderOptions: { // ctx.renderClient('home/home.js', locals); // 这里的 home/home.js 就是基于这个目录 即:app/view/home/home.js // app/view/ 目录下的文件会根据 app/web/page/ 目录下文件生成,即对应的 app/web/page/home/home.vue basedir: path.join(app.baseDir, 'app/view') }, // 渲染时会自动将 这些 css、js 依赖注入到 layout 中 injectRes:[ { url: 'https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.0.2/css/swiper.min.css' }, { url: 'https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.0.2/js/swiper.min.js' } ] }; // ... }

多页面服务器端渲染

比如首页test页面

  • 配置路由:修改app/router.js文件
'use strict'; module.exports = app => { const { router, controller } = app; router.get('/', controller.index.index.index); router.get('/test', controller.index.index.test); // ... };
  • 配置控制器:修改app/controller/index/index.js文件
'usestrict'; const egg = require('egg'); module.exports = class IndexController extends egg.Controller { async index() { const { ctx, service} = this const result = service.article.getArtilceList(); await ctx.render('index/index.js', result); } async test() { const { ctx, service} = this const result = service.article.getArtilceList(); await ctx.render('test/index.js', result); } };
  • 添加首页前端文件:添加app/web/page/index/index.vue文件
<template> <layout title="test" description="vue server side render" keywords="egg, vue, webpack, server side render"> home </layout> </template> <script type="text/babel"> export default { mounted() { console.log('mounted: this.list', this.list) } } </script> <style> /* @import "index.css"; */ </style>
  • 添加test页面前端文件:添加app/web/page/test/index.vue文件
<template> <layout> test </layout> </template>

备注

  • 服务器端渲染会直接将vue文件解析成html代码渲染
  • 需要有一个骨架,这里使用的是layout组件。整体步骤如下
  • 执行渲染首先会进入到app/web/framework/entry/template.js文件中
import Layout from 'component/layout/index'; import plugin from 'framework/plugin'; export default function(Vue) { // 添加国际化,挂载 $request 等 Vue.use(plugin); // 引入 Layout 并注册成一个组件 // 在页面中就可以使用 Layout 这个组件了 Vue.component(Layout.name, Layout); }
  • app/web/framework/entry/template.js文件中注册了一个Layout组件
import Vue from 'vue'; import MainLayout from './main.vue'; import '../../../asset/css/bootstrap.css'; import '../../../asset/css/blog.css'; import createLayout from '../layout'; // 这里使用 createLayout 方法渲染出来一个骨架 export default createLayout('Layout', { MainLayout }, '<div id="app" data-server-rendered="true"><MainLayout><div slot="main"><slot></slot></div></MainLayout></div>');

多页面客户端渲染

  • 将控制器中的render方法或者renderToHtml方法修改成renderClient方法即可

  • 客户端渲染会使用app/web/page/home/layout.html作为骨架文件进行渲染,所以不需要再使用layout组件(当然使用也不会报错)

<!-- app/web/page/test/index.vue --> <template> <div> test </div> </template>

单页面服务器端渲染

比如后台admin

  • 配置路由:修改app/router.js文件
// ... module.exports = app => { const { router, controller } = app; // ... router.post('/admin/api/article/list', controller.admin.admin.list); // 后台的接口 router.post('/admin/api/article/add', controller.admin.admin.add); router.get('/admin/api/article/del/:id', controller.admin.admin.del); router.get('/admin/api/article/:id', controller.admin.admin.detail); router.get('/admin(/.+)?', controller.admin.admin.home); // 后台对应的前端路由 // ... };
  • 修改app/controller/admin/admin.js文件
'use strict'; const egg = require('egg'); module.exports = class AdminController extends egg.Controller { async home(ctx) { const url = ctx.url.replace(/\/admin/, ''); await ctx.render('admin/home/home.js', { ctx, url, title: 'easy-admin' }); } async list(ctx) { this.ctx.body = ctx.service.article.getArtilceList(ctx.request.body); } async add(ctx) { ctx.body = this.service.article.saveArticle(ctx.request.body); } async del(ctx) { const { id } = ctx.params; ctx.body = this.service.article.deleteArticle(id); } async detail(ctx) { const id = ctx.query.id; ctx.body = {}; } };
  • app/web/page/admin/home/目录下添加home.vue文件
<template> <AdminLayout :title="title"> <transition name="fade" mode="out-in"> <router-view></router-view> </transition> </AdminLayout> </template> <script type="text/babel"> import Vue from 'vue'; import ElementUI from 'element-ui'; import VueI18n from 'vue-i18n'; import 'element-ui/lib/theme-chalk/index.css'; import createI18n from 'framework/i18n/admin'; import store from './store/app'; import router from './router'; import AdminLayout from 'component/layout/admin'; Vue.use(VueI18n); Vue.use(ElementUI); export default { router, store, components: { AdminLayout, }, // data() { // return { // title: this.$store.state.title // } // }, computed: { title() { return this.$store.state.title; } }, hook :{ render(context, options) { const i18n = createI18n(context.state.locale); options.i18n = i18n; } }, mounted() {}, }; </script>

备注:

  • app/web/page/admin/home/目录就相当于单页面应用中的src/目录
  • 在该目录下新建router/store/view/component/目录等
  • 控制器中使用的是render方法进行服务器端渲染。所以需要一个layout。这里是app/web/component/layout/admin/index.js

创作不易,若本文对你有帮助,欢迎打赏支持作者!

 分享给好友: