Nuxtjs

SSR渲染

SSR:在服务器端将Vue渲染成HTML返回给浏览器

优点

  • 对SEO支持好
  • SPA单页渲染更快

npx create-nuxt-app nuxtDemo

修改开发地址

package.json文件中进行修改

"config":{
    
    
	"nuxt":{
    
    
		"host":"127.0.0.1",
		"port":"8000"
	}
}

生命周期

请添加图片描述

nuxtServerInit

  • 请求先到达 nuxtServerInit 方法,图中也表明了适用场景是对 store 操作,函数仅在每个服务器端渲染中运行 且运行一次,只能定义在store的主模板当中
  • 定义在store/index.js中
export const actions={
    
    
    nuxtServerInit(store,context){
    
    
        console.log('nuxtServerInit',store,context);
    }
}

middleware

  • 下来请求到达 middleware ,允许您定义一个自定义函数运行在一个页面或一组页面渲染之前。可以运行在全局,或者某个页面组件之前,不会在components组件内部运行
  • 中间件执行顺序
    nuxt.config.js=>匹配布局=>匹配页面
  • 有三中定义方式
    • nuxt.conig.js中引入middleware/auth.js
// auth.js
export default({
     
     store,route,redirect,params,query,req,res})=>{
    
    
    console.log('nuxt.config.js',store,route,redirect,params,query,req,res);
}
// nuxt.conig.js
router:{
    
    
  middleware:'auth'
},
  • 定义在layout默认组件中
	<template>
    <div>
        <p>默认布局</p>
        <nuxt/>
    </div>
</template>
<script>
    export default{
      
      
        // middleware:'auth', // 页面中间级中间件
        middleware(){
      
      
            console.log('layout middleware');
        }
    }
</script>
  • 定义在单个页面中
<template>
  <div>
    <div><nuxt-link :to="{name:'about'}">about</nuxt-link></div>
    <div><nuxt-link :to="{name:'news'}">news</nuxt-link></div>
    <div><nuxt-link :to="{name:'test'}">test</nuxt-link></div>
  </div>
</template>

<script>
export default {
    
    
  name: 'IndexPage',
  middleware(context){
    
    
      console.log('middleware',context);
  },
  // 参数有效性
  validate({
     
     params,query}){
    
    
    // 校验业务
    console.log('validate');
    return true;
  },
  // 读数据返回给组件
  asyncDate(){
    
    
    console.log('asyncDate');
  },
  // 读数据给VUEX
  fetch(){
    
    
    console.log('fetch');
  },
  beforeCreate(){
    
    
    console.log('beforeCreate');
  },
  created(){
    
    
    console.log('created');
  }
}
</script>

asyncData & fetch

接下来达到 asyncData & fetch 方法,asyncData() 适用于在渲染组件前获取异步数据,返回数据后合并到data选项内部,fetch() 适用于在渲染页面前填充 vuex 中维护的数据。会在组件每次加载前被调用(在服务端或切换至目标路由之前),由于是在组件 初始化 前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象

通过axios发送网络请求

  • npm i @nuxtjs/axios
  • nuxt.config.js中进行配置
modules: [
   '@nuxtjs/axios'
 ],
axios:{
    
    
	proxy:true, // 开启代理
},

proxy:{
    
    
   '/api/':{
    
    
     target:"http://localhost:3001", // 代理转发地址
     changeOrigin:true,
     pathRewrite:{
    
    
       '^/api':''
     }
   }
 },
  • 发送请求读取 本地或网络数据
<template>
    <div>
        <h3>商品</h3>
        <div><nuxt-link to="/goods/1">商品1</nuxt-link></div>
        <div><nuxt-link to="/goods/1">商品2</nuxt-link></div>
        <!-- <div>{
    
    {title}}</div> -->
        <nuxt/>
    </div>
</template>
<script>
export default{
      
      
    async asyncData({
       
       $axios}){
      
      
        let list=await $axios({
      
      url:'/data/list.json'}); // 读取本地static/data/list.json中数据
        let list2=await $axios({
      
      url:'/api/home'}); // 读取网络http://localhost:3001数据,目前在3000端口里,通过nuxt配置进行代理
        console.log(list);
        return {
      
      
            title:list.data
        }
    },
    // async fetch({$axios,store}){
      
      
    //     let list=await $axios({url:'/data/list.json'});
    //     store.commit('M_NAME',list)
    //     console.log(list);
    // }
}
</script>

axios配置

  • nuxt.config.js中进行配置,引入axios配置插件
plugins: [
    {
    
    
      src:'~/plugins/axios',
      ssr:true, // 服务端
    }
  ],
  • 在plugins文件夹下新建axios.js
export default function({
     
     $axios,redirect,route,store}){
    
    
    // 基本配置
    $axios.defaults.timeout=3000,
    // 请求拦截
    $axios.onRequest(config=>{
    
    
        config.headers.token='';
        return config;
    })
    // 响应拦截
    $axios.onResponse(res=>{
    
    
        if(res.data.err===2&&route.fullPath!=='login'){
    
    
            redirect('/login?path='+route.fullPath)
        }
        return res;
    })
    // 错误处理
    $axios.onError(error=>{
    
    
        return error
    })
}

render

最后进行渲染。将渲染后的页面返回给浏览器,用户在页面进行操作,如果再次请求新的页面,此时只会回到生命周期中的 middlerware 中,而非 nuxtServerInit ,所以如果不同页面间需要操作相同的数据请用 vuex 来维护,render钩子只能定义渲染时的配置,render内部不可以有业务逻辑,也不执行beforeCreate & created

mounted & updated

运行在客户端
注:没有keep-alive 那自然activated、deactivated这两个生命周期也没了

路由

  • nuxt会把page下的自动生成路由
  • 只需要在跳转时通过nuxt-link配置路径
<template>
  <div>
    <div><nuxt-link :to="{name:'about'}">about</nuxt-link></div>
    <div><nuxt-link :to="{name:'news'}">news</nuxt-link></div>
  </div>
</template>

<script>
export default {
      
      
  name: 'IndexPage'
}
</script>
  • 路由传参和VueRouter一致
<template>
    <div>
        <!-- <div><nuxt-link to="/news/123">123</nuxt-link></div> -->
        <div><nuxt-link :to="{name:'news-id',params:{id:123}}">123</nuxt-link></div>
    </div>
</template>
  • 在对应page页面可以用_表示参数解析

    • page文件夹
      • goods页面
        • _id.vue
        1. 非动态路由可对跳转路由为/goods/1进行解析,解析结果为id:1
        2. 动态路由:to=“{name:‘goods-id’,params:{id:1}}”,解析结果为id:1
  • 可对参数进行校验,如果参数格式不对则进入到nuxt准备的404页面

<template>
   <div>
    <div>new content</div>
    <div>id:{
   
   {$route.params.id}}</div>
    <div><nuxt-link to="/">home</nuxt-link></div>
   </div>
</template>
<script>
    export default{
      
      
        validate({
       
       params}){
      
      
            return /^\d+$/.test(params.id)
        }
    } 
</script>
  • 对路由跳转时添加动画效果
  1. 所有路由配置在全局引入的CSS文件里
	/* 页面进入时,页面离开时 入 */
	.page-enter-active,.page-leave-active{
    
    
    transition: opacity 2s;
	}
	/* 页面离开时 退 */
	.page-enter,.page-leave-active{
    
    
	    opacity: 0;
	}
  1. 单个路由配置在全局引入的CSS文件里
	.test-enter-active,.test-leave-active{
    
    
    transition: all 2s;
    font-size: 12px;
	}
	.test-enter,.test-leave-active{
    
    
	    font-size: 40px;
	}
<template>
    <div>test</div>
</template>

<script>
export default {
      
      
  name: 'TestPage',
  transition:'test', // 对应的页面进行相应配置
}
</script>

路由守卫

  • 前置路由守卫
  1. 全局守卫:nuxt.config指向middleware,layouts定义中间件
  2. 组件独享守卫:middleware
  3. 插件全局后置守卫
  • 后置路由守卫
  1. 使用vue的beforeRouterLeave钩子
  2. 插件全局后置守卫

默认模板和默认布局

默认模板

  • 在项目根目录下,新建一个app.html文件,nuxt默认它是一个默认模板
<!DOCTYPE html>
<html lang="en">
<head>
    {
   
   {HEAD}}
</head>
<body>
    <p>默认模板</p>
    {
   
   {APP}}
</body>
</html>
  • 在项目根目录下建一个layouts文件夹,里面新建一个default.vue
<template>
    <div>
        <p>默认布局</p>
        <nuxt/>
    </div>
</template>

错误页面和个性meta

错误页面

  • 在项目根目录下建一个layouts文件夹,里面新建一个error.vue

个性meta

  • nuxt可在每个页面对head进行单独设置
<template>
   <div>
    <div>new content</div>
    <div>id:{
   
   {$route.params.id}}</div>
    <div><nuxt-link to="/">home</nuxt-link></div>
   </div>
</template>
<script>
    export default{
      
      
        validate({
       
       params}){
      
      
            return /^\d+$/.test(params.id)
        },
        data(){
      
      
            return {
      
      
                title:this.$route.params.title
            }
        },
        head(){
      
      
            return {
      
      
                title:this.title,
                meta:[
                    {
      
      hid:"不覆盖",name:'new1',content:'测试页面'}
                ]
            }
        }
    } 
</script>

loading页面

  • 在进行路由跳转时可以给一些提示,也就是loading
  • 在nuxt.config.js中进行配置
  // 自定义loading效果
  // loading:{color:'#399',heigth:'3px'},
  loading:'~/components/loading.vue'
  • components新建loading.vue
<template>
    <div v-if="loading">loading....</div>
</template>
<script>
export default{
      
      
    data:()=>({
      
      
        loading:false,
    }),
    methods:{
      
      
        start(){
      
      
            this.loading=true;
        },
        finish(){
      
      
            this.loading=false;
        }
    }
}
</script>

vuex

模块方式

  • 在nuxt中集成了vuex
  • store目录下每个js文件都会被转换称为状态树,当然index是根模块

模块数据管理

  • 主模块
// 主模块
// state
export const state=()=>({
    
    
    data1:false,
})
export const mutations={
    
    
    M_UPDATE_DATE1(state,payload){
    
    
        state.data1=payload
    }
}
// actions
export const actions={
    
    
    nuxtServerInit(store,context){
    
    
        // console.log('nuxtServerInit',store,context);
    }
}
// getters
export const getters={
    
    
    getData1(state){
    
    
        return state?'真':'假'
    }
}
  • home模块
export const state=()=>({
    
    
    home1:false,
    test:'',
})
export const mutations={
    
    
    M_UPDATE_HOME1(state,payload){
    
    
        state.home1=payload.home1
    },
    M_UPDATE_TEST(state,payload){
    
    
        state.test=payload.test
    }
}
export const actions={
    
    
    A_UPDATE_HOME({
     
     commit,state},payload){
    
    
        // 可以做异步处理
        commit('M_UPDATE_HOME1',{
    
    home1:true})
    },
    A_UPDATE_TEST({
     
     commit,state},payload){
    
    
        console.log(payload);
        commit('M_UPDATE_TEST',payload)
    }
}
  • 也面中使用
<template>
  <div>
    <div><button @click="changClick">测试home中数据</button></div>
 </div>
</template>

<script>
export default {
      
      
  name: 'IndexPage',
  methods:{
      
      
    changClick(){
      
      
      this.$store.dispatch('home/A_UPDATE_TEST',{
      
      test:'test'});
      this.$store.commit('home/M_UPDATE_HOME1',{
      
      home1:true})
    }
  }
}
</script>
<style scoped>
.app-header-active{
      
      
  color: red;
  background-color: pink;
}
</style>

vuex状态持久化管理

  • npm i cookie-universal-nuxt
  • nuxt.config.js中配置
 modules: [
    '@nuxtjs/axios',
    'cookie-universal-nuxt'
  ],
  // 注册后,在全局上下文对象中多了个$cookies
  • 在登陆页面,在登陆成功后同步数据到cookie和vuex
<template>
    <div>
        <button @click="login">登录</button>
    </div>
</template>
<script>
export default {
    
    
    name:'login',
    methods:{
    
    
        login(){
    
    
            this.$cookies.set('user',{
    
    name:'jack'});
            this.$store.commit('M_UPDATE_USERINFO',{
    
    name:'jack'})
        }
    }
}
</script>
  • 强刷时,nuxtServerInit中将cookie中数据同步带vuex中
// 主模块
// state
export const state=()=>({
    
    
    data1:false,
})
export const mutations={
    
    
    M_UPDATE_DATE1(state,payload){
    
    
        state.data1=payload
    }
}
// actions
export const actions={
    
    
	// 同步数据
    nuxtServerInit(store,{
     
     app:{
     
     $cookies}}){
    
    
        let user =$cookies.get('user')?$cookies.get('user'):{
    
    err:1,token:'',name:''};
        store.commit('user/M_UPDATE_USERINFO',user)
    }
}
// getters
export const getters={
    
    
    getData1(state){
    
    
        return state?'真':'假'
    }
}

引入ElementUI组件库

  • nuxt.config.js中ElementUI以插件方式引入
export default {
    
    
  // Global page headers: https://go.nuxtjs.dev/config-head
  head: {
    
    
    title: 'nuxtDemo',
    htmlAttrs: {
    
    
      lang: 'en'
    },
    meta: [
      {
    
     charset: 'utf-8' },
      {
    
     name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
    
     hid: 'description', name: 'description', content: '' },
      {
    
     name: 'format-detection', content: 'telephone=no' }
    ],
    link: [
      {
    
     rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },


  // Global CSS: https://go.nuxtjs.dev/config-css
  css: [
    '~/asstes/css.css',
    'element-ui/lib/theme-chalk/index.css'
  ],

  // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
  plugins: [
    {
    
    
      src:'~/plugins/axios',
      ssr:true, // 服务端
    },
    {
    
    
      src:'~/plugins/element-ui',
      ssr:true,
    }
  ],

  // Auto import components: https://go.nuxtjs.dev/config-components
  components: true,

  // Modules: https://go.nuxtjs.dev/config-modules
  modules: [
    '@nuxtjs/axios',
    'cookie-universal-nuxt'
  ],

  axios:{
    
    
    proxy:true, // 开启代理
  },

  proxy:{
    
    
    '/api/':{
    
    
      target:"http://localhost:3001", // 代理转发地址
      changeOrigin:true,
      pathRewrite:{
    
    
        '^/api':''
      }
    }
  },

  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {
    
    
    transpile:[/^element-ui/],
  }
}
  • 在plugins中新建element-ui.js
import Vue from 'vue';

// 整体引入
import ElementUI from 'element-ui';
Vue.use(ElementUI);

// 按需引入
// import {Button} from 'element-ui';
// Vue.use(Button);
  • 页面中使用
<template>
  <div>
    <div><el-button type="primary">EL按钮</el-button></div>
 </div>
</template>

<script>
export default {
      
      
  name: 'IndexPage',
}
</script>

全局方法,过滤器, 全局指令,全局组件

全局方法

  • nuxt.config.js中配置
export default {
    
    
  plugins: [
    {
    
    
    src:'~/plugins/mixins'
    }
  ],
}
  • plugins文件夹下新建mixins
// 全局方法
import Vue from 'vue';
const show=()=>console.log('全局方法');
Vue.prototype.$show=show; // 服务端钩子不可以用
  • 在页面中通过this.$show()调用

过滤器

  • nuxt.config.js中配置
  • assets/js新建filters.js文件
export function fillzero(n){
    
    
    return n<10?'0'+n:n+'';
}
export const date=time=>{
    
    
    let d = new Date();
    d.setTime(time);
    const year=d.getFullYear();
    const month=d.getMonth()+1;
    const date=d.getDate();
    return `${
      
      year}${
      
      month}${
      
      date}`
}
  • plugins文件夹下mixins中写入
// 全局方法
import Vue from 'vue';
const show=()=>console.log('全局方法');
Vue.prototype.$show=show; // 服务端钩子不可以用

// 全局过滤器
import * as filters from '../asstes/js/filters';
Object.keys(filters).forEach(key=>Vue.filter(key,filters[key]))
  • 页面中使用
<template>
    <div>
        <div>time:{
    
    {
    
    time|date}}</div>
    </div>
</template>
<script>
export default{
    
    
    data(){
    
    
        return {
    
    
            time:new Date(),
        }
    }
}
</script>

全局指令

  • nuxt.config.js中配置
  • assets/js新建directives.js文件
function direc1(el,binding,vnode){
    
    
    console.log('自定义指令');
}
export default {
    
    
    bind(el,binding,vnode){
    
    
        direc1(el,binding,vnode)
    }
}
  • plugins文件夹下mixins中写入
// 全局方法
import Vue from 'vue';
const show=()=>console.log('全局方法');
Vue.prototype.$show=show; // 服务端钩子不可以用

// 全局过滤器
import * as filters from '../asstes/js/filters';
Object.keys(filters).forEach(key=>Vue.filter(key,filters[key]))

// 自定义指令
import direc1 from '../asstes/js/directives';
Vue.directive('direc1',direc1)
  • 在页面中按正常指令使用
<template>
  <div>
    <div v-direc1>自定义指令</div>
 </div>
</template>

<script>
export default {
      
      
  name: 'IndexPage',
}
</script>

全局组件

  • nuxt.config.js中配置
  • components/global新建globalTest.vue文件
<template>
    <div>全局组件测试</div>
</template>
  • plugins文件夹下mixins中写入
// 全局方法
import Vue from 'vue';
const show=()=>console.log('全局方法');
Vue.prototype.$show=show; // 服务端钩子不可以用

// 全局过滤器
import * as filters from '../asstes/js/filters';
Object.keys(filters).forEach(key=>Vue.filter(key,filters[key]))

// 自定义指令
import direc1 from '../asstes/js/directives';
Vue.directive('direc1',direc1)

// 全局组件
import globalTest from '../components/global/globalTest';
Vue.component('globalTest',globalTest)
  • 页面中按组件正常使用,并且不需要再次引入
<template>
    <div>
        <GlobalTest/>
    </div>
</template>
<script>

export default{
      
      
}
</script>

资源指向

static

  • static中资源不会经过处理,直接搬到资源环境下,映射到资源根目录下
  • 可以通过绝对路径拿到static下资源
<img src='/img.test.png'/>

assets

  • assets下资源会被处理
  • 可以通过相对路径拿到assets下资源
<img src='../assets/img.test.png'/>
<img src='~assets/img.test2.png'/>

猜你喜欢

转载自blog.csdn.net/weixin_64925940/article/details/127251740