安装:
npx create-nuxt-app xx
Choose rendering mode Universal (SSR)
开发:npm run dev
服务器端
服务端和客户端都运行
beforeCreate && created
nuxtServerInit 对stroe操作
middleware
中间件执行流程顺序:
nuxt.config.js->匹配布局->匹配页面
middleware nuxt.config outside->middleware layouts->middleware pages
validate
页面级别
//参数的有效性 validate({params,query}){ //校验业务 console.log('validate' return true; },路由是约定式
展示区: name:路由名目录名-其他目录-文件名 params: key要对等文件名 子路由:目录代表子路由,子路由内部同级的文件,代表是同级一级路由 配置 声明式跳转:<nuxt-link :to=" name: 'product-id' ,params:{id:3] ,query:{a:111,b:222}}">商品e3</nuxt-link>
动态路由,_名称 ,加下划线,代表变量
路径从pages出发为依据
<nuxt-link to="/goods/2?a=11&b=22">商品02</nuxt-link> //id对应文件名为_id.vue <nuxt-link :to="{name:'goods-id' ,params:{id:3}, query:{a:111,b:222}}">商品03/nuxt-link要用二级的内容替换一级的内容,只需要在二级的文件夹内新建index.vue,并把写在外面的路由和展示区转移进去即可
要使进入页面是是页面自身(详情列表)而不是详情页,需要加入index.vue(详情列表)
pages/一级展示/二级展示 /index.vue会在一级展示 /index.vue空文档代表有默认页,不会找寻其他_详情.vue
exact-active-class="xxx",xxx样式,加上exact,代表严格匹配
扩展性路由
router:{ //扩展路由 extendRoutes(routes,resolve){ routes.push( name: ' home', path: ' /index' , component: resolve(___dirname, 'pages/index.vue') ) } }错误页面写在layouts内error.vue
//接受错误信息error:{statuscode,message} props:'error' 编程式导航 <button @click="$router.replace('/index')>跳转到首页</button>在nuxt.config.js内配置对应css全局样式
css:[ 'assets/css/transition.css' ]单独的组件内部引用动画,要写上transition:'动画名'
前置:依赖中间件middlware,插件
全局守卫: nuxt.config指向middleware layouts定义中间件 组件独享守卫: middleware(写法同layouts)
插件全局前置守卫
后置:组件独享后置守卫,使用vue的beforeRouteLeave钩子
插件全局后置守卫
//全局守卫前置业务 //nuxt.config文件 router:[ middleware: 'auth', ] //middleware/auth.js文件 export default ({store,route,redirect,params, query,req,res})=>{ // context服务端上下文 //store状态树信息 //route一条目标路由信息 // redirect强制跳转 // params, query校验参数合理性 console.log('middleware nuxt.config outside') redirect('xx') } ------------------------ //ayouts定义中间件 middleware(istore,route,redirect,params ,query}){ console.log('middleware nuxt.config outside') } ------------------------- //插件全局前置守卫 //nuxt.config文件,~代表根目录 plugins:[ '~/plugins/router' ], //plugins/router.js export default ({app,redirect,params,query,store})=>{ console.log('插件') // app == vue实例 app.router.beforeEach((to,from,next)=>{ -------------------- //全局前置的守卫,插件 //next(true)/next(false) //next( '/login ') × redirect 跳转函数 √ }) --------------------- //插件全局后置守卫 app.router.afterEach((to,from)=>{ console.log('插件全局后置守卫') }) } ------------------------ //组件独享后置守卫 export default { beforeRouteLeave(to,from,next){ let bl=window.confirm('是否要离开'); next(b1) } }安装@nuxtjs/axios、@nuxtjs/proxy
//nuxt.config文件 modules:[ '@nuxtjs/axios' ], plugins: [ '~/plugins/router', { src: '~/plugins/axios', ssr:true//服务端 }, //组件 //$axios可使用modules内定义的 async asyncData({$axios}){ let res = await $axios({url: '/data/list.json'}) console.log('读取到的静态资源',res.data) //此时将数据写入template,利于seo }解决跨域
//nuxt.config文件 axios:{ proxy:true, //允许跨域 //prefix:'api'//baseUrl }, proxy:{ '/api/':{ target: "http://localhost: 3001',//代理转发的地址 changeOrigin:true, pathRewrite:{ '^/api':'' } },loading.vue
<template> <div v-if="loading" >loading...</div> </template> <script> export default { data:()=>({ loading:false }), methods:{ //start和finish是nuxt的loading内置的方法 start(){ this.loading = true; } finish(){ this.loading = false; } } } </script>模块方式: store目录下的每个.js文件会被转换成为状态树指定命名的子模块(当然,index是根模块)
Classic(不建议使用): store/index.js返回创建Vuex.store实例的方法。
state必须是一个函数(规定),其他可以为对象,然后批量导出index内的state,mutations,actions,getters
...mapstate({home:state=>state.home.data}), //名称冲突时可以将data重命名为home安装cookie-universal-nuxt:状态持久化,需要到配置文件的mudules内添加一下,请求自动携带cookie
思想:登录时,
同步vuex 8& cookie,强制刷新后(vuex失效),用nuxtServerInit钩子,取出cookies,同步vuex,axios拦截器读取vuex(vuex存在内存上,读取速度更快;cookies存在磁盘,读取速度慢)1.同步vuex && cookie
this.$cookies.set('user',res.data) this.$store.commit('user/M_UPDATE_USER ,res.data) //跳转: 1.登录或注册跳转到用户页,2.哪里来回哪里 if(!this.$route.query.path || /login|reg/.test(this.$route.query.path)){ this.$router.replace( '/user') }else{ this.$router.replace(this.$route.query.path)2.强刷后,利用nuxtServerInit取出cookies同步vuex
store/index.js
//actions export const actions = { nuxtServerInit(store,{app:{$cookies}}) { //初始化token东西到store当中 let user = $cookies.get('user')?$cookies.get('user'):{err:2,msg:'未登录',token:''} store.commit('user/M_UPDATE_USER',user) } }3.axios拦截器读取vuex
//请求拦截 $axios.onRequest(config=>{ config.headers.token = store.state.user.token return config; }) //取不到token,则发送的请求得到的响应是错误的,可以利用响应拦截跳转到登录也 //响应拦截 $axios.onResponse(res=>{ if(res.data.err === 2 && route.fullPath !== '/login'){ redirect('/login?path= '+route.fullPath) return res })element ui
下载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)nuxt.config.js
css:[ 'element-ui/lib/theme-chalk/index.css' ], plugins: [ { src: "~/plugins/element-ui", ssr:true,//不支持ssr的插件只会在客户端运行不要给true //mode: 'server'//client// v2.44 } ], build:{ transpile:[ /^element-ui/] }反向激活菜单高亮
watch:{ $route:{ immediate:true,//首次运行 handler(route){ let find = false; this.navs.map((item,index)=>{ if(item.path =='/') this.$router.push({name : 'root'}) if(route.path==item.path){ this.activeIndex = index + ''; find=true; } if(!find) this.activeIndex="-1" }) }}用户离开注册页提示
beforeRouteLeave(to,from,next){ if(this.username||this.password){ let bl=window.confirm('是否要离开'); next(bl) }else{ next(true) } }注销
logout(){ //删除cookie,情况vuex this.$cookies.remove( 'user') this.$store.commit( 'user/M_UPDATE_USER',{ err:1, msg:'未登录', token: '', data:{} }) this.$router.push( ' /login ') }plugins/mixins.js定义全局方法
import Vue from 'vue' let show = ()=>console.log('全局方法') Vue.propotype.$show = show //服务端钩子内部不可以使用,this不会执行vue实例组件调用
mouted(){ this.$show() //打印全局方法 }assets/script/filters.js
export function fillzero(n){ //补0 return n < 10 ? '0’ +n : ''+n; } ...过滤器放到插件的文件plugins/mixins.js内
//全局过滤器,导出所有过滤器 import * as filters from '../assets/script/filters'; Object.keys(filters).forEach(key=>Vue.filter(key,filters[key]));组件使用
{{4|fillzero}} //04directive.js
bind绑定的时候触发,inserted插入的时候触发,componentUpdated更新的时候触发
function direc1(el,binding,vnode){ console.log('全局指令1',el,binding,vnode) } export default{ bind(el,binding, vnode){ direc1(al,binding, vnode) } }全局指令放到插件的文件plugins/mixins.js内
import direc1 from '../assets/script/directives'组件使用
<div v-direc1="'red'">direc1</div>定义组件于components/global/uc-button/index.vue,组件名为’uc-button'
全局组件放到插件的文件plugins/mixins.js内,index.vue可以省略写
import UcButton from '../ components/global/uc-bgctorl'; Vue.component('uc-button',UcButton)组件使用
<uc-button/>nuxt.config.js全局引入
网站描述头部信息,优化seo
nuxt.config.js
module.exports = { mode: 'universal', head:{ //环境变量的名称 title:process.env.npm package _name||'统一标题', meta:[ {charset: "utf-8'}, {name: 'viewport ',content: 'width=device-width,initial-scale=1'}, {hid: 'description' ,name: process.env.npm_package_description|| 'description' ,content: ''} ], link:[ {rel: 'icon'.type: 'image/x-icon',href: '/favicon1.ico'} ] } ... }局部特色meta
head(){ return { meta:[ name: 'keywords ' ,content:this.collectionNameD }minxin.js内定义混入方法
Vue.mixin({ methods:{ $seo(title,content,payload = []){ return { title, meta:[{ hid: 'description', name: 'keywords', content }].concat(payload) } } })组件使用
head(){ return this.$seo(this.data.title,this.data.des,[]) }下载node-sass sass-loader
使用
<style lang="scss" scoped> .. </style>全局主题导入
下载@nuxtjs/style-resources,需要配置modules,还需要指定styleResources内的scss文件
styleResources:{ scss:[ './assets/scss/global.scss' ] },assets/scss/gloabl.css
$theme-bg:#393;使用
.box2{ background: $theme-bg }项目根路径下创建app.html文件,约定模板
<! DOCTYPE html> <html {{HTML_ATTRS]}> <head {{HEAD_ATTRS)1 {{HEAD}} <!--加入个性的内容--> </head> <body {{BODY_ATTRS}}> {{APP}} </body> </html>~代表根路径
static无优化,不参与打包
accets打包优化,转base64
<!--相对路径找到一些需要压缩的资源--> <img src="~assets/img/btns.png" alt=""> <!--绝对路径找到一些无需压缩的资源--> <img src="/img/bg.jpg" alt=""> <div class="bgimg"></div> .bgimg{ height: 5opx; background: ur1(~assets/img/takeSbmComment.png) }全局引入资源
公共文件可以在app.html通过src引入可以通过nuxt.config.js内的script:[{src:'...'}]进行添加,或者用link链接阻塞加载
局部引入
head:{ script:[ {src:'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'} ]}https://www.bilibili.com/video/BV13Z4y1T74J?p=29
整个项目重写初始化运行时候会报错
在tsconfig.json内加上"skipLibcheck" : true,
js修改成ts下载@nuxt/typescript-build,配置nuxt.config.js
以下记录部分代表性的修改
buildModules: [‘@nuxt/typescript-build']添加tsconfig.json,types/vue-shim.d.ts配置文件,文件内容在https://typescript.nuxtjs.org/guide/setup.html#configuration
在tsconfig.json内加上
{ "skipLibcheck":true, "experimentalDecorators":true, }下载vue-property-decorator和vue-class-component,用类的方式定义vue组件
ps: 项目必须在工作区首个,vscode的bug
组件内的ts
<script lang="ts"> import {Vue, Component,Watch}from 'vue-propelty-decorator' @Component export default class Loading extends Vue{ //data元数据==实例属性 loading: boolean = false //methods的方法类内的实例方法 start():void{ this.loadiag = true; } //无需,隔开 finish():void{ this.loading = false; } } </script>ts对象数组的处理
type TNavs = {path:string,title:string} @component export default class AppHeader extends Vue{ activeIndex:string = '-1'; navs:TNavs[]=[ {path: '/index',title:'首页'}, {path: '/goods',title:'商品'}, {path: ' /user' ,title:'用户'} ] }watch改装
需要引入Route
import {Route} from 'vue-router' @Watch('$route' ,{immediate:true,deep:true}) onRoutechange(route:Route){ let find=false; this.navs.map((item,index)=>{ if(item.path=='/' ) this.$router.push({name: 'root'}) if(route.path==item.path) { this.activeIndex=index+''; find=true; } }) if(!find) this.activeIndex="-1"; }default.vue改装
@component({ middleware({store,route,redirest,params,query}){ //store状态树信息 //route一条目标路由信息 // redirect强制跳转 //params,query校验参数合理性 console.log( 'middleware layouts全局守卫前置业务') // redirect( ' /reg') }, //引入的组件 components:{AppHeader} })ts需要加上.vue后缀
error.vue改装
import {Vue, Component,Prop}from 'vue-propelty-decorator' @Component export default class Error extends Vue{ @Prop() readonly error:stringlundefined }服务器的钩子(validate,asyncData,transition,head)直接放到component装饰器的内使用
a x i o s = = > a p p . axios == > app. axios==>app.axios
axios配置,才能类型推算,返回值可以写成(res:any)
types/vue.d.ts
import Vue from 'vue'; import {NuxtAxiosInstance} from'@nuxtjs/axios' declare module 'vue/types/vue'{ interface Vue{ $axios:NuxtAxiosInstance; $seo:Function; detail:{title:string,des:string};//实现约定好 $show:()=>void; $cookies: NuxtCookies; } }事件类型Event
类型断言
this.$route.query.path as string <string>this.$route.query.pathhttps://www.bilibili.com/video/BV13Z4y1T74J?p=31
下载vuex-class
import {State,Getter,Action,Mutation} from 'vuex-class'记录部分代表性的修改
index.vue
//改装 //...mapstate(['bNav']), @State bNav:boolean |undefined; // ...mapState({home: state=>state.home.data}), @State(state=>state.home.data)home?:object // ...mapstate( 'user',['data']), @State('user') data!:oject;//外部state.user做组件内的data使用 // ...mapGetters(['getNav']), @Getter getNav!:string; //抓取getters的key,作为组件实例属性使用 // . ..mapActions( 'user',['A_UPDATE_USER']), @Action('user/A_UPDATE_USER') A_UPDATE_USER!:(payload:object)=>voidtypes/index.ts
类型主题模块
interface Istorestate{ bNav:boolean; bLoading: boolean; interface IstoreHome{ err:number; msg?:string; data:Array<{ _id:string; des:string; time :number; title:string; detail?:{ auth:string; auth_icon :str1ng3 content:string; } }> } interface IstoreUser{ interface storeUser{ err: number; msg:string; token:string; data?:Partial<{ //Partial全部加上可选 _id:string; nikename :string; fans:number; follow: number; time:number; icon:string; }> } export {IStoreUser,IStoreState,IstoreHome}stroe/index.js
//改装 import {IStoreState} from '@/types' export const state = function():IStoreState{ return{ bNav: false, bLoading: false }stroe/home.js
//创建个类型约束 type TAcion = { commit:(type:string,payload:object)=>void; state:iStoreHome } export const actions = { A_UPDATE_HOME({commit,state}:TAcion,payload:IStoreHome){ commit( 'M_UPDATE_HOME' ,{err:0,data:{title:"home模块actions所传递的数据"}}) } }https://www.bilibili.com/video/BV13Z4y1T74J?p=32
nuxt通过代理,将请求转发带真实服务器,部署时,nuxt项目即前端工程3000和真实服务器即后端工程9001都要部署。
nuxt通过server/index.js开启自身端口服务3000
nuxt先打包npm run build,放到服务器3000端口的有:.nuxt /server /static /package-lock.json /package.json /nuxt.config.js
真实服务器即后端工程9001
先配置一下nuxt.config.js
module.exports = { mode: 'universal', server:1 port: 3000,//default 3000 host:'0.0.0.0' //因为都被放在服务器上 ... }通过pm2管理阿里云 需要开启安全组:3000,9001 (阿里后台)
远程工具链接阿里云(finallshell)
pm2 start /usr/local/981/bin/www --name=node9001 cd /usr/local/3000/ pm2 --name=nuxt3000 start npm -- run start