零、今天要完成蓝色的部分
image
一、重点温习框架中的几个部分文件 —— 铺垫
1、nuxt.config.js 文件
项目的核心文件,作为一个配置文件,对全局配置起到一个十分重要的作用,像我们的 webconfig 一样
//这些配置在你的项目里可能不一定都有,但是我都会提到module.exports = { cache: {}, css: [ // 加载一个 node.js 模块 // 'hover.css/css/hover-min.css', // // 同样加载一个 node.js 模块,不过我们定义所需的预处理器 // { src: 'bulma', lang: 'sass' }, // // 项目中的 CSS 文件 // '~assets/css/main.css', // // 项目中的 Sass 文件 // { src: '~assets/css/main.scss', lang: 'scss' } // 指定 scss 而非 sass ], // 默认 true dev: process.env.NODE_ENV !== 'production',//不是生产环境 // 创建环境变量 env: {}, // 配置 Nuxt.js 应用生成静态站点的具体方式。 genetate: { dir: '', minify: '', routes: [], }, /* * vue-meta * Headers of the page */ head: { title: '老张的哲学', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: 'Nuxt.js project' } ], link: [ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } ] }, /* ** Customize the progress bar color */ loading: { color: '#3B8070' }, /* ** Build configuration */ build: { /* ** Run ESLint on save */ extend (config, { isDev, isClient }) { if (isDev && isClient) { config.module.rules.push({ enforce: 'pre', test: /\.(js|vue)$/, loader: 'eslint-loader', exclude: /(node_modules)/ }) } } }, performance: { gzip: false, prefetch: true }, // 引入 Vue.use 的插件 plugins: [], // 默认当前路径 rootDir: process.cwd(), router: { base: '', mode: 'history', linkActiveClass: 'nuxt-link-active', scrollBehavior: (to, from, savedPosition) => { // savedPosition 只有在 popstate 导航(如按浏览器的返回按钮)时可以获取。 if (savedPosition) { return savedPosition } else { let position = {} // 目标页面子组件少于两个 if (to.matched.length < 2) { // 滚动至页面顶部 position = { x: 0, y: 0 } } else if (to.matched.some((r) => r.components.default.options.scrollToTop)) { // 如果目标页面子组件中存在配置了scrollToTop为true position = { x: 0, y: 0 } } // 如果目标页面的url有锚点, 则滚动至锚点所在的位置 if (to.hash) { position = { selector: to.hash } } return position } }, // default middleware: 'user-agent', // 扩展路由 extendRoutes: () => {}, // 默认同 rootDir srcDir: this.rootDir, transition: { name: 'page', mode: 'out-in' }, watchers: { chokidar: {}, // 文件监控 webpack: { aggregateTimeout: 300, poll: 1000 } } } }
2. 视图模板
默认的 html 模版: 应用根目录下的 app.html 文件, 如果你没有找到这个文件, 则采用默认的模版,当然你也可以自己新增,配置,
这个更像是我们之前的 index.html 页面,只不过把 <div id='app'></div> 挂载,变成了填充的方式。
<!DOCTYPE html><html {{ HTML_ATTRS }}> <head> {{ HEAD }} </head> <body {{ BODY_ATTRS }}> {{ APP }} </body></html>
3. layouts 布局目录
image
可以修改该目录下的 default.vue 来修改默认布局 , 这个就是类似于我们之前的 app.vue 页面,
<template> <div class="layout-default"> <cl-header></cl-header> <nuxt class="layout-main"/> <cl-footer></cl-footer> <div class="layout-bg"></div> </div></template>
其中 <nuxt/> 是必需的,页面的主体内容会显示在这里 (类似于根节点的 <router-view/>)
此外还可以在目录下 error.vue 作为错误页面,具体的写法可以参考官方文档
4. pages 页面 路由
路由, 约定大于配置, 支持动态, 嵌套, 动态嵌套路由, 过渡效果和中间件,通过文件夹目录名称, 组件名称, 生成路由配置,默认的 transitionName 为 page, 可在 assets 中添加全局的过渡效果,
在匹配页面之前执行;
页面组件实际上是 Vue 组件,只不过 Nuxt.js 为这些组件添加了一些特殊的配置项(对应 Nuxt.js 提供的功能特性)以便你能快速开发通用应用。
<template> <h1 class="red">Hello {{ name }}!</h1></template><script> export default { asyncData (context) { // called every time before loading the component return { name: 'World' } }, fetch () { // The fetch method is used to fill the store before rendering the page }, head () { // Set Meta Tags for this Page }, // and more functionality to discover ... } </script><style> .red { color: red; } </style>
nuxt.config.js --> 执行middleware --> 匹配布局 --> 匹配页面
image
用于存放页面级别的组件,nuxt 会根据该目录下的页面结构生成路由
比如上图中的页面结构,会生成这样的路由配置:
const _7b01ffaa = () => import('..\\pages\\post\\index.vue' /* webpackChunkName: "pages_post_index" */).then(m => m.default || m) const _2b7fe492 = () => import('..\\pages\\post\\_id.vue' /* webpackChunkName: "pages_post__id" */).then(m => m.default || m) const _4f14dfca = () => import('..\\pages\\index.vue' /* webpackChunkName: "pages_index" */).then(m => m.default || m)export function createRouter () { return new Router({ mode: 'history', base: '/', linkActiveClass: 'nuxt-link-active', linkExactActiveClass: 'nuxt-link-exact-active', scrollBehavior, routes: [ { path: "/post", component: _7b01ffaa, name: "post" }, { path: "/post/:id", component: _2b7fe492, name: "post-id" }, { path: "/", component: _4f14dfca, name: "index" } ], fallback: false }) }
5、使用插件 plugins 文件夹
如果项目中还需要引入其他的第三方插件,可以直接在页面中引入,这样在打包的时候,会将插件打包到页面对应的 js 里面,但要是别的页面也引入了同样的插件,就会重复打包。如果没有需要分页打包的需求,这时候可以配置 plugins,然后在根目录的 nuxt.config.js 中添加配置项 build.vendor 和 plugins,这里的 plugins 属性用来配置 vue.js 插件,也就是 可以用 Vue.user() 方法 的插件
默认只需要 src 属性,另外还可以配置 ssr: false,让该文件只在客户端被打包引入,如果是像 axios 这种第三方 (不能 use) 插件,只需要在 plugins 目录下创建 axios.js,然后在 build.vendor 中添加配置 (不需要配置 plugins)
这样在打包的时候,就会把 axios 打包到 vendor.js 中。
二、配置页面,实现首页的数据获取 —— 异步
1、在 static 文件中,新增样式 vue-blog-sq.css 文件
提醒,这里的文件,不会被打包,所以会在页面中呈现原有格式,如果想每次都被编译(可以实现压缩效果),需要写到 assets 资源文件夹中
image
image
2、在 components 中,新建 layout 文件夹,然后新增页头页脚 组件
这个很简单,就是普通的 *.vue 组件写法,大家可以自行下载浏览
image
3、在 layouts 页面布局文件夹中,新增 blog.vue 布局
提醒:以后也可以单给用户增加布局,比如 user.vue
<template> <div class="layout-default"> <cl-header></cl-header> <nuxt class="layout-main"/>//注意,<nuxt />,必须有,类似一个 <router-view/> <cl-footer></cl-footer> </div></template><script type="text/javascript"> import clHeader from "~/components/layout/header.vue"; import clFooter from "~/components/layout/footer.vue"; export default { data () { return {}; }, mounted () { }, components: { clHeader, clFooter } }; </script>
很简单的一个布局入口,是不是很像我们之前的 App.vue 中的 路由容器 —— <router-view /> ,但是又比其更丰富,因为我们之前的 app.vue 只能有一个入口,但是 nuxt 可以提供多个自定义 模板layouts,更方便。
4、根目录新增 config 文件夹,添加 index.js ,作为我们以后的配置文件,类似 .net core api 中的 appsetting.json 文件
const config = { //开发环境配置,开发的时候 development: { //api: "http://localhost:58427/api/", api: "http://123.206.33.109:8081/api/" }, //生产环境配置,部署的时候 production: { api: "" } }; //获取当前环境变量,是 production或者developmentmodule.exports = config[process.env.NODE_ENV];
5、在 plugins 插件中,新增 server_site 文件夹,然后添加 http.js 和 index.js
1、为什么要使用插件?
我们可以在应用中使用第三方模块,一个典型的例子是在客户端和服务端使用 axios 做 HTTP 请求。
首先我们需要安装 npm 包:
npm install --save axios
然后在页面内可以这样使用:
<template> <h1>{{ title }}</h1></template><script> import axios from 'axios' export default { async asyncData ({ params }) { let { data } = await axios.get(`https://my-api/posts/${params.id}`) return { title: data.title } } } </script>
有一个值得注意的问题是,如果我们在另外一个页面内也引用了 axios
,那么在应用打包发布的时候 axios
会被打包两次,而实际上我们只需要打包一次。这个问题可以通过在 nuxt.config.js
里面配置 build.vendor
来解决:
module.exports = { build: { vendor: ['axios'] } }
经过上面的配置后,我们可以在任何页面里面引入 axios
而不用担心它会被重复打包。
2、为什么要分 server_site服务端 与 client_site客户端 插件
有些插件可能只是在浏览器里使用,所以你可以用 ssr: false
变量来配置插件只从客户端还是服务端运行。
举个栗子:
nuxt.config.js:module.exports = { plugins: [ { src: '~/plugins/vue-notifications', ssr: false }//设置ssr 为false ] } plugins/vue-notifications.js://定义一个插件 import Vue from 'vue' import VueNotifications from 'vue-notifications' Vue.use(VueNotifications)
同样地,如果有些脚本库你只想在服务端使用,在 Webpack 打包 server.bundle.js
文件的时候会将 process.server
变量设置成 true
。
3、配置服务端 http.js 和 index.js 的内容
image
// http.js 封装 axios,防止多处打包import Axios from "axios";import config from "~/config";//引入配置文件 // 实例化 axios()const http = Axios.create({ baseURL: config.api,//根url timeout: 8000, validateStatus: function (status) { return status >= 200; } }); // 定义错误异常方法function LogicError (message, code, data) { this.message = message; this.code = code; this.data = data; this.name = "LogicError"; } LogicError.prototype = new Error(); LogicError.prototype.constructor = LogicError; // http 的request 请求http.interceptors.request.use((data, headers) => { return data; }); // http 的response 命令,失败的调用上边的失败异常方法http.interceptors.response.use(response => { const data = response.data; switch (data.success) { case true: return data.data; default: throw new LogicError(data.msg); } }, err => { throw new LogicError("网络请求错误"); });export default http;// 输出http
// 定义 http 插件,是一个全局变量import Vue from "vue";import http from "./http.js";// 引入http封装的axiosconst install = function (VueClass, opts = {}) { // http method VueClass.http = http; VueClass.prototype.$http = http; }; Vue.use(install);// 在vue 中,使用该插件
6、在 nuxt.config.js 中引用我们的服务端插件
这样添加以后,我们就可以全局使用请求命令了,打包的时候,也只会打包一个
提示:1、记得需要按照提示安装 axios npm install --save axios
2、引入的组件库必须配置 plugins, 但是有的组件库不支持 ssr.
image
7、设计修改 pages 下的 index.vue 页面,异步获取接口数据
提示:这个就是文章开头提到的问题
1、为什么要异步?
asyncData
方法会在组件(**限于页面组件,也就是pages 文件夹下的vue文件 **)每次加载之前被调用。它可以在服务端或路由更新之前被调用。 在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,你可以利用asyncData
方法来获取数据,Nuxt.js 会将asyncData
返回的数据融合组件data
方法返回的数据一并返回给当前组件。注意:由于
asyncData
方法是在组件 初始化 前被调用的,所以在方法内是没有办法通过this
来引用组件的实例对象。Nuxt.js 提供了几种不同的方法来使用
asyncData
方法,你可以选择自己熟悉的一种来用:
返回一个
Promise
, nuxt.js会等待该Promise
被解析之后才会设置组件的数据,从而渲染组件.使用 async 或 await (推荐使用)
返回 Promise
export default { > asyncData ({ params }) { return axios.get(`https://my-api/posts/${params.id}`) > .then((res) => { return { title: res.data.title } > }) > } > }
使用 async或await
export default { async asyncData ({ params }) { > let { data } = await axios.get(`https://my-api/posts/${params.id}`) > return { title: data.title } > } > }
<!-- index.vue 页面 --><template> <div class="u-marginBottom40 js-collectionStream"> <div class="streamItem streamItem--section js-streamItem"> <div class="u-clearfix u-maxWidth1000 u-marginAuto"> <div class="row u-marginTop30 u-marginBottom20 u-sm-marginLeft20 u-sm-marginRight20 u-xs-marginTop0 u-xs-marginRight0 u-xs-marginLeft0"> <div v-for="post in blogs" :key="post.bID" class="postArticle postArticle--short is-withAccentColors"> <!--......--> </div> </div> </div> </div> </div></template><script> import Vue from "vue";//引入 vue 实例,获取全局变量 export default { layout: "blog",//这个就是我们自定义的模板布局 async asyncData (ctx) {// asyncData() 异步获取数据方法 let blogs = []; try { console.log(1) const blogData = await Vue.http.get("Blog?page=1&bcategory=技术博文"); console.log(blogData); blogs = blogData; return { blogs: blogs, }; } catch (e) { //ctx.error({ statusCode: 500, message: "出错啦" });//自定义错误页面 } }, data () { return {}; }, head () {//针对每一个页面,进行封装 head return { meta: [ { name: "description", content: "老张的哲学是个人博客,利用NUXT.js的服务端渲染方案" } ] }; }, mounted () {}, filters: {//过滤器,用来过滤时间字符串 timeFormat: function (time) { if (!time) return ""; return time; } }, methods: {}, components: { } }; </script>
8、启动项目,就能看到我们的数据了
提示:如果报 less 错误,请安装
npm install less less-loader
作者:SAYLINING
链接:https://www.jianshu.com/p/6c83b29dc12f