zhh10 / Notes

前端中react、Vue、ES6、css3、webpack、模块化等思维导图
12 stars 1 forks source link

Nuxt #30

Open zhh10 opened 4 years ago

zhh10 commented 4 years ago

1. Nuxt安装及结构目录

Nuxt.js是一个基于Vue.JS的通用应用框架

通过对客户端/服务端基础架构的抽象组织,Nust主要关注的是应用的UI渲染

安装vue-cli

npm install vue-cli -g

初始化

npx create-nuxt-app <app-name>

启动

npm run dev

目录结构和配置文件

常用配置

  1. 配置端口

    nuxt.config.js中配置

    config:{
    nuxt:{
    port:"8080"
    }
    }
  2. 每一个页面都要初始化一个css

    nuxt.config.js中配置一个css选项

~是一个别名,~assets意思是在根目录下找到assets文件夹

css:[
    '~assets/css/reset.css'
]
  1. 配置webpack
zhh10 commented 4 years ago

2. 默认模版和默认布局

  1. 默认模版

在根目录下新建一个文件app.html,就是nuxt的默认模版

<!DOCTYPE html>
<html lang="en">
<head>
{{ HEAD }}
// 这个head会在nuxt.config.js中定义了
</head>
<body>
<p>zhh10 github</p>
{{ APP }}
// 引入page文件夹下的文件
</body>
</html>

修改默认模版后要重启一下服务器

  1. 默认布局

layouts/default.vue中,默认布局无法设置head

<template>
  <div>
    <ul>
      <li>首页</li>
      <li>直播</li>
      <li>点播</li>
    </ul>
    <Nuxt />
  </div>
</template>

<style>
ul::after{
  display:block;
  content:'';
  clear:both;
}
li{
  list-style:none;
  float:left;
  margin:10px 10px;
}
</style>
zhh10 commented 4 years ago

3. 路由讲解和参数传递

推荐使用nuxt-link

<nuxt-link :to="{name:'index'}">home</nuxt-link>
<nuxt-link :to="{name:'about'}">about</nuxt-link>
<nuxt-link :to="{name:'news'}">news</nuxt-link>

嵌套路由

news文件夹下再建立一个newsItem文件


**动态嵌套路由**
- page
  - news
    - index
    - newsItem (记得在父级使用`<nuxt-child></nuxt-child>`)
    - newsItem/
      - _id.vue
      - index.vue(默认`nuxt-child`内容)
**参数传递**
``` **参数接收** `this.$route.params.newsId` **动态路由** - news/ - _id.vue - index.vue ``` ``` **参数校验** ``` export default{ validate(params){ return /^\d+$/.test(params.params.id) //如果返回的是true 就返回设置好的动态页面 //如果返回的是false,就返回nuxt准备的404页面 } } ```
zhh10 commented 4 years ago

4. 路由动画效果

全局配置

在assets/css里面配置(跳转得用nuxt-link)

.page-enter-active,.page-leave-active{
transition: opacity 2s;
}
.page-enter,.page-leave-to{
opacity:0;
}
.page-leave,.page-enter-to{
opacity:1;
}

单独设置 在css文件中设置

.test-enter-active,.test-leave-active{
transition:all 2s;
font-size:12px;
}
.test-enter,.test-leave-to{
opacity:0;
font-size:40px;
}

vue文件中设置

export default {
transition:'test'
}
zhh10 commented 4 years ago

5. 错误页面和个性meta设置

error.vue

通过error.vue设置错误页面

<template>
<div>
<div v-if="error.statusCode===404">
404页面,你需要的页面没有找到!
</div>
<div v-else>
500页面
</div>
</div>
</template>
<script>
export default{
props:['error'],
}
</script>

个性head

export default{
    validate(params){
        return /^\d+$/.test(params.id)
    },
    // 通过head方法,设置页面的独特head 
    head(){
        return{
            title:`直播间${this.$route.params.id}`,
            meta:[
                {hid:'description',name:'Live',content:'This is a LiveItem'},
            ]
        }
    }
}
zhh10 commented 4 years ago

6. middleware

中间件可以在自定义函数在渲染页面之前运行

第一种方法在nuxt.config.js中用

nuxt.config.js

router:{
    middleware:'auth',
}

middleware/auth.js

export default (context)=>{
    // context 服务端上下文
    // store route redirect params query req res
    // 全局守卫业务
    console.log('nuxt.config.js   middleware')
}

第二种方法在布局层layout中设置

layouts/default.vue

export default {
    // middleware:'auth'
    middleware(){
        console.log('layout middleware')
    } 
    //运行在nuxt.config.js之后
}

第三种方法运行在页面级别

Live.vue

export default {
    // middleware:'auth',

    // 函数形式 只有加载这个页面时,才会运行这个中间件
    // 运行在页面实例化之前
    middleware(){
        console.log('Live middleware')
    }
}

中间件执行流程顺序:

  1. nuxt.config.js
  2. 匹配布局
  3. 匹配页面

上面操作打印顺序为:

  1. nuxt.confjg.js middleware
  2. layout middleware
  3. Live middleware
zhh10 commented 4 years ago

7. 路由守卫

前置

1. 依赖中间件middleware的情况

全局守卫:
  1. nuxt.config.js 指向middleware

middleware/auth.js

export default({store,route,redirect,params,query,req,res})=>{
    // store vuex状态信息
    // route 目标路由信息
    // redirect 强制跳转
    // params,query 校验参数合理性
    redirect('/login')
}
  1. layouts定义中间件
组件独享守卫:

middleware方法

2. 依赖plugins的情况

插件全局前置守卫

plugins: 在运行Vue.js应用程序之前执行的JS插件

nuxt.config.js

plugins:['~/plugins/router']

plugins/router.js

export default ({app,redirect})=>{
    // app == Vue实例
    app.router.beforeEach((to,from)=>{
        // 可以使用next('/login')
        // 也可以使用redirect('/login')

        if(to.name === 'login' || to.name === 'reg'){
            next()
        }else{
            redirect({name:'login'})
        }
    })
}

后置

使用vue的beforeRouteLeave钩子

reg.vue

这个时候可以访问windows对象了,因为浏览器已经把它渲染出来了

export default {
    beforeRouteLeave(to,form,next){
        let bl = window.confirm('是否要离开')
        next(bl)
    })
}
  1. 插件全局后置守卫
    export default ({app,redirect} => {
    app.router.afterEach((to,from)=>{
        console.log(to.name,from.name)
    })
    })
zhh10 commented 4 years ago

8. 扩展路由

<nuxt-link to="/" active-class="app_header--active">首页</nuxt-link>
<nuxt-link to="/goods" active-class="app_header--active">首页</nuxt-link>

.app_header--active{
    background:#399;
    color:#fff;
}

严格匹配

<nuxt-link exact-active-class="...." ></nuxt-link>

nuxt.config.js

router:{
    middleware:'auth',
    // 扩展路由
    extendRoutes(routes,resolve){
        routes.push({
            name:'home',
            path:'/index',
            component:resolve(__dirname,'pages/index.vue')
        })
    }
}

自定义错误信息

layout/error.vue

<template>
    <div>
        <h1 v-if="error.statusCode">
            {{error.message}}
        </h1>

    </div>
</template>
<script>
export default {
    props:['error']
}
</script>
zhh10 commented 4 years ago

9. 数据交互、跨域

安装

  1. @nuxtjs/axios
  2. @nuxtjs/proxy
    npm i @nuxtjs/axios @nuxtjs/proxy

1.配置nuxt.config.js

modules:[
    '@nuxtjs/axios',
    '@nuxtjs/proxy'
    ]

asyncData或fetch请求数据

添加进来的模块会以$开头

async asyncData({$axios}){
    let res = await $axios.get(url:'...')
    return {
        title:res.data.title
    }
},
async fetch({store,$axios}){
    let res = await $axios.get(url:'...')

}

请求跨域

配置nuxt.config.js

modules:[
    '@nuxtjs/axios',
    '@nuxtjs/proxy'
],
axios:{
    proxy:true,//开启axios跨域
    prefix:'/api',//baseUrl
},
proxy:{
    '/api':{
        target:'http://localhost:3001',//代理转发地址
        changeOrigin:true,
    }
}
async asyncData({$axios}){
    let res = await $axios.get({url:'/api/home'})

}

拦截器设置

nuxt.config.js

plugins:[
    {   src:'~plugins/axios',
        ssr:true
    }
]

plugins/axios

export default function({$axios,redirect,route,store}){
    // 基本配置
    $axios.default.timeout = 1000
    // 请求拦截
    $axios.onRequest(config => {
        console.log('请求拦截')
        config.headers.token = '...'

        return config
    })
    // 响应拦截
    $axios.onResponse(res => {
        if(res.data.err === 2 && route.fullPath !== '/login'){
            redirect('/login?path='+route.fullPath)
        }
        return res.data
    })

    // 错误处理
    $axios.onError(error => {
        return error
    })
}
zhh10 commented 4 years ago

10. 自定义loading页面

  1. 定义系统默认loading效果 nuxt.config.js
    loading:{
    color:'#399',
    height:'3px',//滚动条高度
    }
  2. 指定一loading组件
    loading:'~/components/loading.vue'

    components/loading.vue

    <template>
    <div v-if="loading">
        ...loading
    </div>
    </template>
    <script>
    export default {
    data:()=>{
        loading:false
    },
    methods:{
    //  这两个方法名是nuxt约定好的,不能更改
        start(){
            this.loading = true
        },
        finish(){
            this.loading = false
        }
    },
    }
    </script>
zhh10 commented 4 years ago

11. 生命周期

  1. nuxtServerInit() 服务器初始化
    • Store action
  2. middleware() 中间件运行
    • nuxt.config.js
    • matching layout
    • matching page & children
  3. validate() 校验参数
    • Pages & children
  4. asyncData() & fetch() 异步数据处理
    • Pages & children
  5. Render 开始客户端渲染
  6. Vue生命周期
    • beforeCreated
    • created

nuxtServerInit

store/index.js

actions:{
    nuxtServerInit({commit},{req}){
        if(req.session.user){
            commit('user',req.session.user)
        }
    }
}

middleware

第一种方法在nuxt.config.js中用

nuxt.config.js

module.exports = {
    router:{
        middleware:'auth',
    }
}

auth.js

export default (context)=>{
    // context 服务端上下文
    // store route redirect params query req res
    // 全局守卫业务
}

第二种方法在布局层layout中设置

export default {
    // middleware:'auth'
    middleware(){
        console.log('middleware')
    } //运行在nuxt.config.js之后
}

第三种方法运行在页面级别

index.vue

export default {
    // middleware:'auth',

    // 函数形式 只有加载这个页面时,才会运行这个中间件
    // 运行在页面实例化之前
    middleware(){

    }
}

中间件执行流程顺序:

  1. nuxt.config.js
  2. 匹配布局
  3. 匹配页面

validate

定义在页面组件

validate({params,query}){

}

asyncData

读数据,返回给组件

async(){
    return {b:2}
}

fetch

读取服务断数据提交给vuex

fetch({store}){

}

服务端的钩子都可以拿到服务端的上下文context

在客户端可以访问window,和组件对象this

服务端的this是undefined

SSR && CSR

CSR window this

zhh10 commented 4 years ago

12. 状态持久化和token验证

安装插件

npm i cookie-universal-nuxt --save

nuxt.config.js

modules:[
    "@nuxtjs/axios",
    "cookie-universal-nuxt"
]

上下文就多出了一个$cookie

思想:登录时,同步vuex && cookie,强制刷新后,nuxtServerInit钩子,取出cookies,同步vuex,axios拦截器读取vuex

login(){
    this.$axios({
        url:'/api/login',
        method:'post',
        data:{
            username:'xxx',
            password:123
        }
    }).then(res => {
        this.$cookies.set('user',res.data)
        this.$store.commit('user/User',res.data)

        this.$router.push('/')
    })
}
export const actions = {
    nuxtServerInit({store,$cookies}){
        let user = $cookies.get('user') ? $cookies.get('user') : {err:'未登录'}

        store.commit('user/User',user)
    }
}

/plugins/axios

export default function({$axios,redirect,route,store,$cookies}){
    config.header.token = store.state.user.token 
    retunr config 
}
zhh10 commented 4 years ago

13. 全局方法、过滤、指令、组件、样式

全局方法

nuxt.config.js

plugins:['~/plugins/mixins']

plugins/mixins

import Vue from "vue"
let show = ()=>{console.log(123)}

Vue.prototype.$show = show 
// 服务端钩子内部不可使用,this不会执行vue实例

全局过滤器

export function filter(n){
    return n<10 ? '0' + n : ''+n
}
export const data = time => {
    let d = new Date() 
    d.setTime(time) 
    let year = d.getFullYear() 
    let month = d.getMonth() + 1 
    let hour = d.getHours() 
    let min = d.getMinutes() 
    let sec = d.getSeconds() 
    return .....
}

Vue.filter

import * as filters from './assets/scripts/filter.js'
Object.keys(filters).forEach(key => {
    Vue.filter(key,filters[key]
})

指令

function direc1(el,binding,vnode){
    console.log(123)
}
export default {
    inserted(el,binding,vnode){
        direc1(el,binding,vnode)
    }
}
import direc1 from "./assets/scripts/direc1"
Vue.directive('direc1',direc1)

全局组件

import UCButton from "../components/global/ucbutton.vue"

Vue.component('UCButton',UCButton)

全局样式

nuxt.config.js

css:['assets/css/base.css']

混入methods

Vue.mixin({
    methods:{
        $seo(title,content,payload = []){
            return {
                title,
                meta:[{
                    hid:'description',
                    name:'keywords',
                    content
                }].concat(payload)
            }
        }
    }
})
head(){
    this.$seo(......)
}
zhh10 commented 4 years ago

14. scss使用

安装完就可以在vue中写scss了

npm i node-sass sass-loader

若想设置全局scss文件

npm i @nuxtjs/style-resources --save

指定全局scss文件

modules:['@nuxtjs/style-resource'],
styleResource:{
    'scss':['./assets/scss/global.scss']
}
zhh10 commented 4 years ago

15. 打包资源

打包

nuxt generate

生成一个dist文件夹

npm install -g live-server
live-server

需要注意的是,在任何 Vue 组件的生命周期内, 只有 beforeCreate 和 created 这两个方法会在 客户端和服务端被调用。其他生命周期函数仅在客户端被调用。

zhh10 commented 4 years ago

16. Vuex

store 目录下的每个 .js 文件会被转换成为状态树指定命名的子模块

使用状态树模块化的方式,store/index.js 不需要返回 Vuex.Store 实例,而应该直接将 state、mutations 和 actions 暴露出来:

export const state = ()=>({
    count:0
})

export const mutations = {
    add(state){
        console.log(123)
        state.count ++ 
    }
}
<p>{{$store.state.dian.count}}</p>

import {mapMutations} from "vuex"
export default {
    methods:{
        ...mapMutations({'add':'dian/add'}),
        handleClick(){
            console.log(this.$store.state)
            this.add()
        }
    }