做百度网站多少钱,如何做外贸网站的推广,如何登陆网站服务器,集美建设局中心网站Nuxt.js简单介绍
2016 年 10 月 25 日#xff0c;zeit.co 背后的团队对外发布了 Next.js#xff0c;一个 React 的服务端渲染应用框架。几小时后#xff0c;与 Next.js 异曲同工#xff0c;一个基于 Vue.js 的服务端渲染应用框架应运而生#xff0c;我们称之为#xff1…Nuxt.js简单介绍
2016 年 10 月 25 日zeit.co 背后的团队对外发布了 Next.js一个 React 的服务端渲染应用框架。几小时后与 Next.js 异曲同工一个基于 Vue.js 的服务端渲染应用框架应运而生我们称之为Nuxt.js。
Nuxt.js 是一个基于 Vue.js 的通用应用框架。通过对客户端/服务端基础架构的抽象组织Nuxt.js 主要关注的是应用的 UI渲染。Nuxt.js 预设了利用Vue.js开发服务端渲染的应用所需要的各种配置。
为什么使用Nuxt.js?
SSR服务端渲染的页面初始加载时间显然优于单页首屏渲染可以方便的对 SEO 进行管理无需配置页面路由内置 vue-rouer自动依据 pages 目录结构生成对应路由配置便捷的 HTML 头部标签管理vue-meta项目结构自动代码分层支持静态化本文将着重以此展开介绍
项目创建
为了便于大家快速使用Nuxt.js 提供了很多模板
starter-template: 基础Nuxt.js模板
typescript-template: 基于Typescript的Nuxt.js模板
express-template: Nuxt.js Express
koa-template: Nuxt.js Koa
adonuxt-template: Nuxt.js AdonisJS
electron-template: Nuxt.js Electron …
等等更多的可以在这里看到 nuxt-community
这里我们使用 starter-template可以使用 vue-cli 安装
$ npm install -g vue-cli
$ vue init nuxt-community/starter-template nuxt-demo
$ cd nuxt-demo
$ npm install生成项目结构如下
nuxt-demo/
├── assets/
├── components/
│ └── AppLogo.vue
├── layouts/
│ └── default.vue
├── middleware/
├── pages/
│ └── index.vue
├── plugins/
│ └── README.md
├── static/
│ └── favicon.ico
├── store/
├── nuxt.config.js
├── package.json
└── README.md可以看出来项目结构还是比较清晰的接着我们根据业务需求在 vue-cli 脚手架生成的项目基础上扩展和修改出来的目录结构如下(已隐去部分文件)
nuxt-demo/
├── api/ //- 接口
│ └── index.js
├── assets/ //- 需要编译的静态资源如 scss、less、stylus
│ ├── images/ //- 图片
│ └── styles/ //- 样式
├── build/ //- 自定义的一些编译配置
├── components/ //- 公用的组件
│ ├── dm-toast.vue //- 全局组件dm-toast
│ └── ...
├── data/ //- 静态数据
├── layouts/ //- 布局
│ ├── components/
│ │ ├── dm-footer.vue //- 公用header
│ │ └── dm-header.vue //- 公用footer
│ └── default.vue //- 默认布局
├── middleware/ //- 中间件
├── mixins/ //- Vue mixins
├── pages/ //- 页面
│ ├── index.vue //- 主页
│ └── ...
├── plugins/ //- vue插件
│ └── dm-tracker.js/ //- 挂载utils/tracker.js
├── static/ //- 无需编译处理的静态资源
│ └── images/ //- 这里存放了一些通过数据循环出来的图片
├── store/ //- vuex
│ └── index.js
├── utils/ //- 工具集
│ ├── index.js
│ ├── http.js //- axios
│ ├── tracker.js //- PV统计
│ └── tracker-uitl.js
├── vendor/ //- 第三方的库和插件
│ └── index.js
├── nuxt.config.js //- Nuxt.js配置文件
├── seo.config.js //- SEO相关配置文件
├── package-lock.json //- npm的版本锁
├── package.json
└── README.md项目配置
Nuxt.js 默认的配置涵盖了大部分使用情形可通过 nuxt.config.js 来覆盖默认的配置下面相关配置根据实际项目驱动讲解未涉及到的配置项可查阅 Nuxt.js 文档。
nuxt.config.js 总览
module.exports {//- Document Common headhead: {meta: [title: 我是一个title,{ charset: utf-8 },{ name: viewport, content: widthdevice-width, initial-scale1 },{ name: renderer, content: webkit },{ name: applicable-device, content: pc },{ http-equiv: X-UA-Compatible, content: IEedge,chrome1 },{ http-equiv: Cache-Control, content: no-transform },{ http-equiv: Cache-Control, content: no-siteapp }],link: [{ rel: icon, type: image/x-icon, href: 你的icon地址 }],//- 这里可以写一些每个页面需要额外引入的一些js代码比如百度统计//- alert(1) 仅为代码示例script: [{type: text/javascript,innerHTML: alert(1)}],//- __dangerouslyDisableSanitizers 设置script中的内容不被转义。//- https://github.com/declandewet/vue-meta#__dangerouslydisablesanitizers-string__dangerouslyDisableSanitizers: [script]}//- 页面切换的时候进度条的颜色loading: { color: #77b6ff },//- modules 可以用来扩展核心功能或者添加一些集成//- 这里使用了一个本地开发请求远端接口的反向代理模块 nuxtjs/proxy//- https://nuxtjs.org/api/configuration-modulesmodules: [nuxtjs/proxy],//- 上面 modules 中配置了 nuxtjs/proxy 时此字段才会生效//- https://github.com/nuxt-community/proxy-moduleproxy: {/api: http://xxx.xxx.com},//- 在这里注册 Vue 的插件、全局组件或者其他的一些需要挂载到 Vue 原型下面的东西//- ssr 为 false 表示该文件只会在浏览器端被打包引入//- https://nuxtjs.org/api/configuration-pluginsplugins: [~plugins/dm-toast,{ src: ~plugins/dm-tracker, ssr: false }],//- 配置全局样式文件每个页面都会被引入//- lang 可以为该样式文件配置相关 loader 进行转译css: [animate.css,{ src: ~assets/styles/common.scss, lang: scss }],//- 配置 Nuxt.js 应用生成静态站点的具体方式。//- https://nuxtjs.org/api/configuration-generategenerate: {//- 为动态路由添加静态化//- 静态化站点的时候动态路由是无法被感知到的//- 所以可以预测性的在这里配置routes: [/1,/2,/3...]},//- router 属性让你可以个性化配置 Nuxt.js 应用的路由vue-router//- https://nuxtjs.org/api/configuration-routerrouter: {//- 中间件在每次路由切换前被调用middleware: set-env,//- 通过 extendRoutes 来扩展或者修改 Nuxt.js 生成的路由表配置extendRoutes(routes) {}},//- 编译配置build: {//- 使用 webpack-bundle-analyzer 分析并可视化构建后的打包文件//- 你可以基于分析结果来决定如何优化它analyze: true,//- 为客户端和服务端的构建配置进行手动的扩展处理//- https://nuxtjs.org/api/configuration-build#extendextend(config, { isDev, isClient, isServer }) {if (isDev isClient) {//- 使用 ESLint 保证代码规范config.module.rules.push({enforce: pre,test: /\.(js|vue)$/,loader: eslint-loader,exclude: /(node_modules)/})}if (!isDev) {config.module.rules //- 覆盖默认 url-loader 配置.find((rule) rule.loader url-loader).options.name images/[name].[ext]?v[hash:7]}},//- 这里可以自定义打包后的文件名//- hash 项目中任何一个文件改动后就会被重新创建//- chunkhash 是根据模块内容计算出的hash值对应的文件发生内容变动就会重新计算//- 生成如下//- head//- ...//- link href//cdn.xxx.com/manifest.js?v8d09730 relpreload asscript//- link href//cdn.xxx.com/vendor.js?v8d09730 relpreload asscript//- link href//cdn.xxx.com/app.js?vfea3ec0 relpreload asscript//- link href//cdn.xxx.com/pages_index.js?v6f7b904 relpreload asscript//- ...//- /headfilenames: {manifest: js/manifest.js?v[hash:7],vendor: js/vendor.js?v[hash:7],app: js/app.js?v[chunkhash:7],//- chunk 这里这样使用编译会报错最后面会讲解相关解决方案chunk: js/[name].js?v[chunkhash:7]},//- 自定义 postcss 配置//- https://nuxtjs.org/api/configuration-build#postcsspostcss: [require(autoprefixer)({browsers: [ 1%, last 3 versions, not ie 8]})],//- 这里可以设置你的CDN地址生成的静态资源将会基于此CDN地址加上URL前缀publicPath: //cdn.xxx.com/,//- Nuxt.js 允许你在生成的 vendor.js 文件中添加一些模块以减少应用 bundle 的体积//- 这里说的是一些你所依赖的第三方模块 (比如 axios)或者使用频率较高的一些自定义模块//- https://nuxtjs.org/api/configuration-build#vendorvendor: [axios,...]}
}路由(router)
Nuxt.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置。
比如
├── pages/
│ ├── b-case/
│ │ ├── home.vue
│ │ ├── home/
│ │ │ └── _type.vue
│ │ └── _id.vue
│ └── index.vue生成路由配置如下
[{name: b-case-home,path: /b-case/home,component: E:\\\\nuxt-demo\\\\pages\\\\b-case\\\\home.vue,chunkName: pages/b-case/home,children: [{name: b-case-home-type,path: :type?,component: E:\\\\nuxt-demo\\\\pages\\\\b-case\\\\home\\\\_type.vue,chunkName: pages/b-case/home/_type}]},{name: b-case-id,path: /b-case/:id?,component: E:\\\\nuxt-demo\\\\pages\\\\b-case\\\\_id.vue,chunkName: pages/b-case/_id},{name: index,path: /,component: E:\\\\nuxt-demo\\\\pages\\\\index.vue,chunkName: pages/index}
]如果你想修改已有路由配置可以在 nuxt.config.js 中添加 route.extendRoutes 配置项覆盖已有路由或者添加新的路由
module.exports {...router: {...extendRoutes(routes) {//- routes 是一个包含所所有路由配置信息的参数}}
}Nuxt模块(modules)
如果你对上述路由的配置方式不满意想要更加个性化的自定义路由Nuxt.js 社区提供了一款 Nuxt模块 router-module
使用步骤
首先安装这个模块
npm install nuxtjs/router --save # OR yarn add nuxtjs/router然后在 nuxt.config.js 中添加模块名 到 modules 字段下
module.exports {...modules: [nuxtjs/router]
}在项目根目录下创建 routes.js并 export 一个方法 createRouter。这里有一点要记住不能使用 pages/ 这个目录会和 Nuxt.js 官方的路由机制产生冲突。
import Vue from vue
import Router from vue-router
import Home from ../views/home.vueVue.use(Router)export function createRouter() {return new Router({mode: history,routes: [{path: /,component: Home}]})
}其他比较常用的 Nuxt.js 模块
axios-moduleaxios模块它将网络请求与页面的进度条集成
proxy-module反向代理模块本地便捷调试远端接口
auth-module鉴权模块
python-module用 Python 编写 Nuxt.js 应用程序
pwa-module把你的站点变成 pwa 渐进式网络应用
配置方法比较类似这里不作详细讲解
插件(plugins)
插件可以让我们向 Vue 注入一些使用率比较高的属性或者方法这里我们讲解一个埋点的插件是如何实现的。
在讲解埋点之前我们需要先了解一下PV的概念PV(page view)即页面浏览量或点击量PV之于网站就像收视率之于电视。
通过 plugins 配置项我们可以轻而易举的在 Vue 中使用插件。同时我们需要在 plugins 目录下创建对应的文件以保证配置项可以正确的加载这个文件。 plugins/dm-tracker.js //- 将发起pv统计的方法挂载到 Vue 原型下
//- 让每个组件都能通过 this.$tracker 访问import Vue from vue
import { trackerPlugins } from ../utils/tracker.jsVue.use(trackerPlugins)utils/tracker.js 里是一些发起 PV 统计所调用的代码隐去了部分业务代码。 utils/tracker.js .../*** 发起PV统计* param {String} caFrom - ev: ca_from* param {Object} vueRouteName - vm.$route.name* return {Promise} 成功then或者失败catch的回调*/
const tracker (caFrom, vueRouteName) {...
}export default trackerexport const trackerPlugins {install(Vue, options) {Vue.prototype.$tracker tracker}
}我们发现几乎每次初始化进入或者跳转到一个新的页面都需要调用 this.$tracker 这个方法要是在每个页面文件里加岂不是很麻烦况且如果将埋点深入到业务层后期维护更新不免产生一些不必要的繁琐所以我们在 layout/default.vue 里面对 $route 进行监听同时设置 watch 参数 immediate: true便可针对每个页面实现这个功能。 layout/default.vue ...mounted() {//- 通过监听路由变化得到不同页面的 pv 统计参数同时 immediate: true 使得每次页面初始进来也会默认执行一次this.$watch($route, ({ name }) this.$tracker(-, name), { immediate: true })}
...至此就完成了一个 PV 统计插件。
页面元信息(head)
Nuxt.js 文档是这么说的 使用 head 方法可以设置当前页面的头部标签在 head 方法里可通过 this 关键字来获取组件的数据所以你可以利用页面组件的数据来个性化设置 meta 标签。为了避免子组件中的 meta 标签不能正确覆盖父组件中相同的标签而产生重复的现象建议利用 hid 键为 meta 标签配一个唯一的标识编号。请阅读关于 vue-meta 的更多信息。
官方示例
templateh1{{ title }}/h1
/templatescript
export default {data () {return {title: Hello World!}},head () {return {title: this.title,meta: [{ hid: description, name: description, content: My custom description }]}}
}
/script根据官方文档的描述我们了解到页面里的 head 配置优先级高于 nuxt.config.js 中的 head就是说同等的配置会覆盖 nuxt.config.js 中的 head 相关位置的配置。但是这个等同覆盖的条件是你为它设置了同一个 hid它会以此作为等同替换的条件去查找相关 dom 元素进行替换。
因为项目生成的是多页面静态站点很多页面需要配置的 meta 多少有些不一样深入到每一个页面去写单独的配置信息不免繁琐了许多所以我们可以在 nuxt.config.js 的 head 字段中将公用的一些 head 信息放在里面所以我们是不是可以把它单独抽离出来作为一个配置文件并和每个页面的路由名字($route.name)关联起来这样按照理想格式的 seo.config.js 就诞生了。
seo的配置文件写好了接下来我们应该怎么才能注入这个配置呢很简单只需要在 default.vue 的 head 字段下将不同页面的配置将他们关联起来。这样就达到了通过每个页面的路由名字($route.name)来映射和渲染对应的 meta。 layout/default.vue templatedivdm-header/nuxt/dm-footer//div
/templatescript
import DmHeader from ./components/dm-header
import DmFooter from ./components/dm-footerconst heads seo function getHeadsMap() {const map {}for (const key in seo) {map[key] seo[key].head}return map
}const routeMapHead heads(require(../seo.config))export default {components: { DmHeader, DmFooter },computed: { routeMapHead },head() {//- SEO 的中心化管理, 根据路由 $route.name 映射 Document headconst route this.$routeconst head this.routeMapHead[route.name]return typeof head function ? head(route) : head}
}
/scriptseo.config.js //- 根据路由 $route.name 映射配置
//- path - 页面的访问路径
//- head - Document head页面的元信息
module.exports {index: {head: {title: 我是首页,meta: [{ hid: keywords, name: keywords, content: },{ hid: description, name: description, content: },{ name: mobile-agent, content: formatwml; url//该页面对应的移动端网址/ },{ name: mobile-agent, content: formatxhtml; url//该页面对应的移动端网址/ },{ name: mobile-agent, content: formathtml5; url//该页面对应的移动端网址/ }],link: [{ rel: alternate, type: applicationnd.wap.xhtmlxml, media: handheld, href: //该页面对应的移动端网址/ }]}},//- head 可以是一个 function//- function 中会接收过来一个参数 route表示当前页面的路由信息//- 可以根据这个做一些动态的配置信息//- 比如动态路由生成的页面中的 meta 信息可能会根据页面内容来决定name: {head(route) {const titles {1: ofo小黄车,2: 盒马生鲜,3: 顺丰速运}return {title: ${titles[route.params.id]}_客户案例-斗米网,meta: [{ name: mobile-agent, content: formatwml; url//该页面对应的移动端网址${route.fullPath} },{ name: mobile-agent, content: formatxhtml; url//该页面对应的移动端网址${route.fullPath} },{ name: mobile-agent, content: formathtml5; url//该页面对应的移动端网址${route.fullPath} }],link: [{ rel: alternate, type: applicationnd.wap.xhtmlxml, media: handheld, href: //该页面对应的移动端网址${route.fullPath} }]}}}...
}页面布局(layout)
Nuxt.js 中抽象出来一个新的概念layout这样将页面划分为三层1. layout、2. page、3. component很方便的在多种布局方案中切换。
------------------------------
| layout |
| |
| ---------------------- |
| | page | |
| | | |
| | -------------- | |
| | | component | | |
| | -------------- | |
| | | |
| | -------------- | |
| | | component | | |
| | -------------- | |
| | | |
| | -------------- | |
| | | component | | |
| | -------------- | |
| ---------------------- |
| |
------------------------------在页面 pages/*.vue 文件中可以指定一种布局不指定的时候会使用默认布局 default。
比如以下目录结构
├── layouts/ //- 布局
│ ├── components/
│ │ ├── dm-footer.vue //- 公用header
│ │ └── dm-header.vue //- 公用footer
│ ├── box.vue
│ └── default.vue //- 默认布局
├── pages/ //- 页面
│ └── index.vue使用 box.vue 的布局nuxt/ 对应页面部分类似 Vue 的 slot layouts/box.vue templatedivdm-header/nuxt/dm-footer//div
/templatescript
import DmHeader from ./components/dm-header
import DmFooter from ./components/dm-footerexport default {components: { DmHeader, DmFooter }
}
/scriptpages/index.vue script
export default {layout: box...
}
/script状态管理(vuex)
像普通的 Vue 应用一样在 Nuxt.js 中也可以使用 vuex而且无需额外 npm install vuex --save 和配置只要直接在项目根目录创建 store 文件夹Nuxt.js 会自动去寻找下面的 .js 文件并自动进行状态树的模块划分。
Nuxt.js 支持两种使用 store 的方式
普通方式返回一个 Vuex.Store 实例感觉很眼熟有木有 store/index.js import Vue from vue
import Vuex from vuexVue.use(Vuex)const store () new Vuex.Store({state: {counter: 0},mutations: {increment (state) {state.counter}}
})export default store模块方式store 目录下的每个 .js 文件会被转换成为状态树指定命名的子模块index.js 会被作为根模块 store/index.js export const state () ({counter: 0
})export const mutations {increment (state) {state.counter}
}store/todos.js export const state () ({list: []
})export const mutations {toggle (state, todo) {todo.done !todo.done}
}最终渲染出来的状态树
new Vuex.Store({state: { counter: 0 },mutations: {increment (state) {state.counter}},modules: {todos: {state: {list: []},mutations: {toggle (state, { todo }) {todo.done !todo.done}}}}
})一键静态化
npm run generate然后会在项目根目录生成 dist 目录静态化后的资源都在其内(html、js、css…)
当然在静态化的时候还是遇到了一些问题我们有一个专门放置静态资源的 CDN 分发服务器所以每次版本更新的时候需要版本智能更新(不然用户访问到的可能就是旧的资源)即对应的模块内容发生改变时才去更新这个版本号虽然可以通过 js/[name].[chunkhash:7].js这样的配置实现但是 CDN 访问到的静态资源是通过 git 控制上线的所以这样生成的静态资源文件名每次可能不一样这样就会越来越多git 需要定时清理比较麻烦。于是才有了这样一个两全的方案既控制了版本更新也让文件名不产生变动。
就是如下配置
module.exports {...build: {...filenames: {...chunk: js/[name].js?v[chunkhash:7]}}
}想法很丰满现实很骨感在 nuxt.config.js 中使用这样的配置静态化编译的时候会报错 Cannot find module pages_index.js?v6f7b904...(⊙o⊙)…经过一系列的源码追踪发现是vue-server-renderer 这个模块的 BUG。
虽然向官方提了相关 issue 但是因不可抗拒的因素暂时无法修复。
vue-server-renderer/server-plugin.js 78行 asset.name.match(/.js$/) 这个判断里的正则明显把我们设定的格式 js/[name].js?v[chunkhash:7] 过滤掉了不会走这个判断所以我们只要想办法把这个判断条件改一下让它走这里。发现他同文件上面有个 isJS 函数这样就省事多了我们可以直接调用。
所以我们如果改源码的话只需要把这里 asset.name.match(/.js$/) 替换为 isJS(asset.name) 就行了。但是改源码总归不好因为并不能保证团队其他成员的机器上模块库一致。投机取巧既然直接修改不好那就间接修改呗。
开始动手先安装一个 npm 的模块
npm install shelljs -D # OR yarn add shelljs -D接着在项目下新建两个文件
build/nuxt-generate.js用来执行静态化的一些命令build/vue-server-renderer.patch.js给 vue-server-renderer 模块打补丁 build/nuxt-generate.js const shell require(shelljs)
const { resolve } require(path)
const nuxt resolve(__dirname, ../node_modules/.bin/nuxt)
const logProvider require(consola).withScope(nuxt:generate)shell.exec(npm run patch, (code, stdout, stderr) {if (code ! 0) {logProvider.error(stderr)}//- 上面的命令执行成功之后在执行下面的命令shell.exec(${nuxt} generate)
})build/vue-server-renderer.patch.js const { resolve } require(path)
const fs require(fs)
const SSRJSPath resolve(__dirname, ../node_modules/vue-server-renderer/server-plugin.js)
const consola require(consola)
const logProvider consola.withScope(vue:patch)module.exports VueSSRPatch()/*** 对 vue-server-renderer/server-plugin.js 源码内容进行替换* asset.name.match(/\.js$/)* * isJS(asset.name)*/
function VueSSRPatch() {//- 检测该模块是否存在if (fs.existsSync(SSRJSPath)) {let regexp /asset\.name\.match\(\/\\\.js\$\/\)/let SSRJSContent fs.readFileSync(SSRJSPath, utf8)//- 检测是否存在需要替换的内容(通常是指该项目在本机第一次运行)if (regexp.test(SSRJSContent)) {logProvider.start(发现vue-server-renderer模块开始执行修补操作)SSRJSContent SSRJSContent.replace(regexp, isJS(asset.name))fs.writeFileSync(SSRJSPath, SSRJSContent, utf8)logProvider.ready(修补完毕)return true}logProvider.warn(该模块已修补过无需再次修补可直接运行\npm run dev\ 或 \npm run gen\)return false}logProvider.warn(未发现该模块跳出本次修复)return false
}最后在 package.json 中 scripts 添加 gen 和 patch 两条命令
scripts: {dev: nuxt,generate: nuxt generate,patch: node build/vue-server-renderer.patch,gen: node build/nuxt-generate
}patch本机第一次运行或者更新相关模块(vue-server-renderer)时需要执行一次。
gen npm run patch 和 npm run generate 的合并命令就是说会先后执行这两个方便本机第一次使用。如果本机执行过 npm run patch可直接 npm run generate生成相关静态页。