这段时间使用 ts 和 vue 做了一个项目,项目从 0 开始搭建,在建设和优化的同时,实现了很多自己的想法,有那么一两个组件可能在我本人看来有意义,所以从头回顾一下当初的想法,同样也可以做到一个记录的作用。如果还没有使用过 ts 的同学可以通过使用 Vue Cli3 + TypeScript + Vuex + Jest 构建 todoList 这边文章开始你的 ts 之旅,后续代码也是在 todoList 的结构上改进的
vue 路由中的懒加载
你真的用好了路由的懒加载吗?
在 2.x 的文档中、cli 的初始化项目中都会默认生成一个路由文件,大致如下:
{ path: '/about', name: 'about', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ './views/About.vue') }
通过路由懒加载的组件会在 webpack 打包之后生成名为 about 的 dist/js/about.39d4f7ae.js 文件。
但是在 react 中,react-loadable 可以使路由在懒加载之前先加载一个其他的组件(一般是 loading )过度这个加载的过程。
A higher order component for loading components with promises.
其实这也就是 react 的高阶组件 (HOC),那么根据 HOC 的思想,我们能否在 vue 中也实现这样一个 HOC 呢?答案是 YES
让我们看一下:
const AsyncComponent = () => ({ // The component to load (should be a Promise) component: import('./MyComponent.vue'), // A component to use while the async component is loading loading: LoadingComponent, // A component to use if the load fails error: ErrorComponent, // Delay before showing the loading component. Default: 200ms. delay: 200, // The error component will be displayed if a timeout is // provided and exceeded. Default: Infinity. timeout: 3000 })
这个 2.3+ 新增的功能,使我们的开始有了可能,我们创建一个 loadable.ts 的高阶组件,利用 render 函数生成组件并返回。
import LoadingComponent from './loading.vue'; export default (component: any) => { const asyncComponent = () => ({ component: component(), loading: LoadingComponent, delay: 200, timeout: 3000 }); return { render(h: any) { return h(asyncComponent, {}); } }; };
在 routes 中使用该组件
import loadable from './loadable'; const routes = [ { path: '/about', name: 'about', // component: () => import(/* webpackChunkName: "about" */ './views/About.vue') component: loadable( () => import(/* webpackChunkName: "about" */ './views/About.vue') } ]
看起来貌似已经成功了,但是在这当中还存在问题。
关于 vue-router ,不可避免的会涉及到路由的钩子函数,但是在以上用法中路由钩子是失效的,why ?
路由钩子只直接生效于注册在路由上的组件。
那么通过 loadable 生成渲染的组件中 About 组件已经是一个子组件,所以拿不到路由钩子。
组件必须保证使用上的健壮性,我们换一种方案,直接返回这个组件。
const asyncComponent = importFunc => () => ({ component: importFunc(), loading: LoadingComponent, delay: 200, timeout: 3000 });
我们重新更换 routes :
const routes = [ { path: '/about', name: 'about', // component: () => import(/* webpackChunkName: "about" */ './views/About.vue') component: asyncComponent( () => import(/* webpackChunkName: "about" */ './views/About.vue') } ]
上述用法已经解决了路由钩子的问题,但是仍然有两点值得注意:
asyncComponent 接受的参数是一个 function , 如果直接写成 import(/* webpackChunkName: "about" */ './views/About.vue'), 则 LoadingComponent 无法生效。
AsyncComponent 还可以添加一个 error 的组件,形成逻辑闭环。
SVG 、Iconfont 在 vue 项目中最优雅的用法
能用 svg 的地方尽量不使用图片 笔者在使用 svg 的时候一开始是使用vue-svg-loader, 具体用法,请自行查看。
但是在写 sidebar 时,笔者想将 svg 通过配置文件的形式写入,让 sidebar 形成多层的自动渲染。
显然 vue-svg-loader 的用法不合适。我们先了解 svg 的用法,我们可以看一篇乃夫的介绍:。
SVG symbol ,Symbols let you define an SVG image once, and reuse it in multiple places.
和雪碧图原理类似,可以将多个 svg 合成一个,但是这里用 id 来语意化定位图标