6

一、引入并创建nuxt项目

确认已经安装 npx ( npx 依附于 npm 5.2.0 安装引入)

npx create-nuxt-app <my-project>

或者在 npm v6.1版本后 可以这样创建:

npm init nuxt-app@latest <my-project>

或者用: yarn:

yarn create nuxt-app <my-project>

二、引入axios库

1、使用nuxt自带模块

npm i @nuxtjs/axios -s

1) 、在 nuxt.config.js 中引入

export default {
  /*
   ** Runtime Config
   */
  publicRuntimeConfig: {
    axios: {
      baseURL: 'https://api.nuxtjs.dev'
    }
  },
  /*
   ** Modules - https://nuxtjs.org/docs/2.x/directory-structure/modules
   */
  modules: ['@nuxtjs/axios']
}

2) 、页面内使用 $axios 获取数据,并用 $config 获取 API 接口的 URL

async asyncData({$axios}) {
  let { res } = await $axios.get(`https://xxx.com/api/xxx`) 
  console.log(res)    
}

3) 、设置公共拦截

~/plugins 创建 axios.js 文件

export default function ({store, redirect, req, router, $axios })  {
    // baseUrl可以在上面的配置中,也可在这儿配置
    $axios.defaults.baseURL = 'http://XXX/api';
    if(process.server){
        // 获取服务端的token,对应函数请自行封装
        var token = getcookiesInServer(req).token;
    }
    if(process.client){
        // 获取客户端token,对应函数请自行封装
        var token = getcookiesInClient('token');
    }
    // request拦截器
    $axios.onRequest(config => {
        if(process.client){
            // 客户端下,请求进度条开始
            NProgress.start();
        }
        // 将获取到token加入到请求头中
        config.headers.common['Authorization'] = token;
    });
    // response拦截器,数据返回后,可以先在这里进行一个简单的判断
    $axios.interceptors.response.use(
        response => {
            if(process.client){
                // 客户端下, 请求进度条结束
                NProgress.done();
            }
            // return response
            if(response.data.code == 401){
                // 返回401,token验证失败
                removeToken("token");
                  // 重定向到登录页面, 这里做一个判断,容错:req.url 未定义
                if(req.url){
                    redirect("/sign?ref="+req.url)
                }else{
                    redirect("/sign")
                }
            }else if(response.data.code == 404){
                // 重定向到404页面
                redirect("/")
            }
            else{
                // 请求接口数据正常,返回数据
                return response
            }
        },
        error => {
            if(process.client){
                NProgress.done();
            }
            if(error.response.status == 500){
                // http状态500,服务器内部错误,重定向到500页面
                redirect("/500.htm")
            }
            if(error.response.status == 404){
                // http状态500,请求API找不到,重定向到404页面
                redirect("/404.html")
            }
            return Promise.reject(error.response)   // 返回接口返回的错误信息
        })
}

2、外部引入axios

npm i axios -s

1) 、创建request文件夹

在文件夹内创建 http.js文件, urls文件夹, apis文件夹

http.js

import axios from 'axios'
import Vue from 'vue'

const ajax = axios.create({
    baseURL: process.env.baseUrl,
    timeout: 30 * 1000
})

// 请求拦截器
ajax.interceptors.request.use(
    config => {
        // const Token = getToken()
        // if (Token) {
        //     config.headers['token'] = getToken()
        // }
        config.headers['Content-Type'] = 'application/json;chartset=utf-8'
        // config.headers["Authorization"] = "Bearer atwerjjhqkwehtjhsdfqwehjhwrgqre";
        return config
    },
    error => {
        throw new Error(`请求错误: ${error}`)
    }
)
// 响应拦截器
ajax.interceptors.response.use(
    response => {
        if (response.status === 200) {
            // 处理返回流文件报错
            if (response.config.responseType === 'blob') {
                var reader = new FileReader()
                reader.readAsText(response.data)
                reader.onload = e => {
                    const result = JSON.parse(e.target.result)
                    if (result.code !== 200) {
                        Vue.prototype.$message.error(result.msg)
                    }
                }
            }
            if (response.data.code === 200) {
                return response.data.data
            } else {
                Vue.prototype.$message.error(response.data.message)
                return Promise.reject(response.data)
            }
        } else {
            return response
        }
    },
    error => {
        throw new Error(error)
        // throw new Error(`请求错误: ${error}`)
    }
)

/*
 * @params {config} 参数从API传递过来
 * @params @{config} {url} 请求地址
 * @params @{config} {data} 请求数据
 */
export function get(config) {
    let obj = {
        url: config.url,
        method: 'get',
        params: {
            ...config.data,
        }
    }
    return ajax(obj)
}

export function post(config) {
    let obj = {
        url: config.url,
        method: 'post',
        data: {
            ...config.data,
        }
    }
    return ajax(obj)
}

export function upload(config) {
    let obj = {
        url: config.url,
        method: 'post',
        data: config.data,
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    }
    return ajax(obj)
}

urls文件夹下创建对应模块的url信息

例如 urls/user.js

export default {
    userinfo: '/user/userinfo',
    userList: '/user/userList'
}

apis文件夹下创建对应模块的api函数

例如 apis/user.js

import { get, post } from '../http.js'
import user from '../urls/user'

export function getUserinfo(params) {
    return get({ url: userUrls.userinfo, params })
}

export function getUserList(params) {
    return post({ url: userUrls.userList, params })
}

页面内正常使用我们熟知的方式引入api函数去调接口获取数据

3、axios跨域配置

1) 、使用官方axios模块时的配置

export default {
    axios: {
        proxy: true,
        prefix: '/api', // baseURL
        credentials: true,
        retry: { retries: 3 }
    },
    proxy: {
        '/api': {
            target: 'http://192.168.xxx.xxx:xxxx', // 代理地址
            changeOrigin: true,
            pathRewrite: {
                '^/api': '', //将 /api 替换掉
            }
        },
    }
}

2) 、使用官方axios模块,引入官方模块 @nuxtjs/proxy 配置

npm i @nuxtjs/proxy -D

nuxt.config.js 中配置

modules: [
    '@nuxtjs/axios',
    '@nuxtjs/proxy'
],
proxy: [
    [
        '/api', 
        { 
            target: 'http://localhost:3001', // api主机
            pathRewrite: { '^/api' : '/' }
        }
    ]
]

3) 、使用外部引入的方式

配合后端部署设置nginx

三、登录状态持久化

npm i -S cookie-universal-nuxt

nuxt.config.js 中配置

modules: [
    // 登录状态持久化
    ['cookie-universal-nuxt', { parseJSON: true }]
],

store配置请参考本文下面的i18n部分, store/index.js

四、引入ant-design-vue组件库

npm i -S ant-design-vue
npm install babel-plugin-import --save-dev

1、按需引入配置

// nuxt.config.js
{
    build: {
        babel: {
            plugins: [
                [
                    'import',
                    {
                        libraryName: 'ant-design-vue',
                        libraryDirectory: 'es',
                        style: true
                        // 默认不使用该选项,即不导入样式 , 注意 ant-design-vue 使用 js 文件引入样式
                        // true 表示 import 'ant-design-vue/es/component/style'
                        // 'css' 表示 import 'ant-design-vue/es/component/style/css'
                    }
                ]
            ]
        }
    }
}

~/plugins 文件夹创建 antd-ui.js 文件

import Vue from 'vue'

Vue.config.productionTip = false

import { Button } from 'ant-dsign-vue'

Vue.use(Button)

2、antd-icon 过大

不是所有的图标我们都能用到

~/plugins 文件夹创建 antd-icons.js 文件

export {
    // 需要使用到的 Icons
    InfoCircleFill,
    DownOutline,
    UpOutline,
    RightOutline,
    LeftOutline
} from '@ant-design/icons'
// nuxt.config.js
const CompressionPlugin = require('compression-webpack-plugin')
const path = require('path')

export default {
    build: {
        vendor: ['axios', 'ant-design-vue'],
        // 解决less加载使用不了的问题
        loaders: {
            less: {
                javascriptEnabled: true
            }
        },
        analyze: {
            analyzerMode: 'static'
        },
        // 使用Babel与特定的依赖关系进行转换
        transpile: [/ant-design-vue/],
        extend(config, ctx) {
            // 配置eslint
            if (ctx.isClient) {
                config.module.rules.push({
                    enforce: 'pre',
                    test: /\.(js|vue)$/,
                    loader: 'eslint-loader',
                    exclude: /(node_modules)/
                })
                // ant-design-vue 的icon组件,按需引入需要的图标,文件太大
                config.resolve.alias['@ant-design/icons/lib/dist$'] = path.resolve(__dirname, './plugins/antd-icons.js') // 引入需要的
            }
        },
        // 打包构建优化
        plugins: [
            new CompressionPlugin({
                test: /\.js$|\.html$|\.css/, // 匹配文件名
                threshold: 10240, // 对超过10kb的数据进行压缩
                deleteOriginalAssets: false // 是否删除原文件
            })
        ],
        optimization: {
            splitChunks: {
                minSize: 10000,
                maxSize: 250000
            }
        }
    }
}

注意: 这儿安装 compression-webpack-plugin 插件会报错,指定插件版本就行了

npm i --save-dev compression-webpack-plugin@6.1.1

提醒:图标使用<a-icon type="close"></a-icon>来显示

如果有组件内使用的,例如

<a-rate v-model="val">
    <a-icon #character type="star"></a-icon>
</a-rate>

五、国际化

1、引入i18n

npm  i vue-i18n --save

2、在 plugins 下创建 i18n.js

// nuxt.config.js
export default {
    plugins: [
        '@/plugins/antd-ui',
        { src: '@/plugins/lazy-load', ssr: false },
        '@/plugins/i18n.js',
        { src: '~/plugins/lodash.js', ssr: false },
        { src: '~/plugins/moment.js', ssr: false },
        { src: '@/plugins/vue-swiper.js', mode: 'client' },
    ],
}
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)

export default ({ app, store }) => {
    // Set i18n instance on app
    // This way we can use it in middleware and pages asyncData/fetch
    app.i18n = new VueI18n({
        locale: store.state.locale,
        fallbackLocale: store.state.locale,
        messages: {
            'en-US': require('@/language/en-US.json'),
            'zh-CN': require('@/language/zh-CN.json')
        },
        silentTranslationWarn: true
    })

    app.i18n.path = link => {
        // 如果是默认语言,就省略
        if (app.i18n.locale === app.i18n.fallbackLocale) {
            return `/${link}`
        }
        return `/${app.i18n.locale}/${link}`
    }
}

3、在 middleware 下创建 i18n.js

// nuxt.config.js
export default {
    router: {
        middleware: ['i18n']
    },
}
export default function({ isHMR, app, store, route, params, error, redirect }) {
    const defaultLocale = app.i18n.fallbackLocale
    // If middleware is called from hot module replacement, ignore it
    if (isHMR) return
    // Get locale from params
    const locale = params.lang || defaultLocale

    if (store.state.locales.indexOf(locale) === -1) {
        return error({ message: '页面未找到.', statusCode: 404 })
    }
    // Set locale
    // store.commit('SET_LANG', locale)
    app.i18n.locale = store.state.locale
    // If route is /<defaultLocale>/... -> redirect to /...
    if (locale === defaultLocale && route.fullPath.indexOf('/' + defaultLocale) === 0) {
        const toReplace = '^/' + defaultLocale + (route.fullPath.indexOf('/' + defaultLocale + '/') === 0 ? '/' : '')
        const re = new RegExp(toReplace)
        return redirect(route.fullPath.replace(re, '/'))
    }
}

4、在 store 下创建 index.js

import { getToken } from '@/lib/token.js'
const state = () => ({
    token: '',
    locales: ['en-US', 'zh-CN'],
    locale: 'en-US'
})

const mutations = {
    setToken(state, token) {
        state.token = token
    },
    SET_LANG(state, locale) {
        if (state.locales.indexOf(locale) !== -1) {
            state.locale = locale
        }
    }
}

const actions = {
    async nuxtServerInit({ commit }, { app }) {
        let token = getToken(app) || ''
        commit('setToken', token)
        //还可以获取一些通用信息,比如个人信息,通用配置什么的
    }
}
export default {
    state,
    actions,
    mutations
}

5、在 language 下创建 en-US.jsonzh-CN.json

更多语言库,请按照规则添加

配置对应的变量属性名称,在页面用

<span>{{ $t('变量名') }}</span>

对应antd-vue组件库国际化,官方推荐使用 config-provider 组件

// default.vue页面
<template>
    <a-config-provider :locale="lang">
        <div class="layout-container">
            <a-layout>
                <a-layout-header>header</a-layout-header>
                <a-layout-content><Nuxt /></a-layout-content>
                <a-layout-footer>footer</a-layout-footer>
                <a-back-top />
            </a-layout>
        </div>
    </a-config-provider>
</template>
<script>
import { mapState } from 'vuex'
import language from '../language/antd-lang/index'
import moment from 'moment'
import 'moment/locale/zh-cn'
moment.locale('en')

export default {
    data() {
        return {}
    },
    computed: {
        ...mapState({
            locale: state => state.locale
        }),
        lang: function() {
            let l = null
            switch (this.locale) {
                case 'zh-CN':
                    l = language.zhCN
                    moment.locale('zh-cn')
                    break
                case 'en-US':
                    l = language.enUS
                    moment.locale('en')
                    break
            }
            return l
        }
    }
}
</script>

language 下创建 antd-lang/index.js, 包含所有的支持语言库

// 阿拉伯
import arEG from '~/node_modules/ant-design-vue/es/locale/ar_EG'
// 保加利亚语
import bgBG from '~/node_modules/ant-design-vue/es/locale/bg_BG'
// 加泰罗尼亚语
import caES from '~/node_modules/ant-design-vue/es/locale/ca_ES'
// 捷克语
import csCZ from '~/node_modules/ant-design-vue/es/locale/cs_CZ'
// 德语
import deDE from '~/node_modules/ant-design-vue/es/locale/de_DE'
// 希腊语
import elGR from '~/node_modules/ant-design-vue/es/locale/el_GR'
// 英语
import enGB from '~/node_modules/ant-design-vue/es/locale/en_GB'
// 英语(美式)
import enUS from '~/node_modules/ant-design-vue/es/locale/en_US'
// 西班牙语
import esES from '~/node_modules/ant-design-vue/es/locale/es_ES'
// 爱沙尼亚语
import etEE from '~/node_modules/ant-design-vue/es/locale/et_EE'
// 波斯语
import faIR from '~/node_modules/ant-design-vue/es/locale/fa_IR'
// 芬兰语
import fiFI from '~/node_modules/ant-design-vue/es/locale/fi_FI'
// 法语(比利时)
import frBE from '~/node_modules/ant-design-vue/es/locale/fr_BE'
// 法语
import frFR from '~/node_modules/ant-design-vue/es/locale/fr_FR'
// 冰岛语
import isIS from '~/node_modules/ant-design-vue/es/locale/is_IS'
// 意大利语
import itIT from '~/node_modules/ant-design-vue/es/locale/it_IT'
// 日语
import jaJP from '~/node_modules/ant-design-vue/es/locale/ja_JP'
// 韩语/朝鲜语
import koKR from '~/node_modules/ant-design-vue/es/locale/ko_KR'
// 挪威
import nbNO from '~/node_modules/ant-design-vue/es/locale/nb_NO'
// 荷兰语(比利时)
import nlBE from '~/node_modules/ant-design-vue/es/locale/nl_BE'
// 荷兰语
import nlNL from '~/node_modules/ant-design-vue/es/locale/nl_NL'
// 波兰语
import plPL from '~/node_modules/ant-design-vue/es/locale/pl_PL'
// 葡萄牙语(巴西)
import ptBR from '~/node_modules/ant-design-vue/es/locale/pt_BR'
// 葡萄牙语
import ptPT from '~/node_modules/ant-design-vue/es/locale/pt_PT'
// 斯洛伐克语
import skSK from '~/node_modules/ant-design-vue/es/locale/sk_SK'
// 塞尔维亚
import srRS from '~/node_modules/ant-design-vue/es/locale/sr_RS'
// 斯洛文尼亚
import slSI from '~/node_modules/ant-design-vue/es/locale/sl_SI'
// 瑞典语
import svSE from '~/node_modules/ant-design-vue/es/locale/sv_SE'
// 泰语
import thTH from '~/node_modules/ant-design-vue/es/locale/th_TH'
// 土耳其语
import trTR from '~/node_modules/ant-design-vue/es/locale/tr_TR'
// 俄罗斯语
import ruRU from '~/node_modules/ant-design-vue/es/locale/ru_RU'
// 乌克兰语
import ukUA from '~/node_modules/ant-design-vue/es/locale/uk_UA'
// 越南语
import viVN from '~/node_modules/ant-design-vue/es/locale/vi_VN'
// 简体中文
import zhCN from '~/node_modules/ant-design-vue/es/locale/zh_CN'
// 繁体中文
import zhTW from '~/node_modules/ant-design-vue/es/locale/zh_TW'

export default {
    arEG,
    bgBG,
    caES,
    csCZ,
    deDE,
    elGR,
    enGB,
    enUS,
    esES,
    etEE,
    faIR,
    fiFI,
    frBE,
    frFR,
    isIS,
    itIT,
    jaJP,
    koKR,
    nbNO,
    nlBE,
    nlNL,
    plPL,
    ptBR,
    ptPT,
    skSK,
    srRS,
    slSI,
    svSE,
    thTH,
    trTR,
    ruRU,
    ukUA,
    viVN,
    zhCN,
    zhTW
}

六、scss、less样式变量全局使用

SASS: `yarn add sass-loader node-sass`
LESS: `yarn add less-loader less`
Stylus: `yarn add stylus-loader stylus`
yarn add @nuxtjs/style-resources
npm i @nuxtjs/style-resources --save-dev

配置请参考:@nuxtjs/style-resources

// nuxt.config.js
export default {
    // 下面这两个配置用一个就行了
    modules: ['@nuxtjs/style-resources'],
    buildModules: ['@nuxtjs/style-resources'],
    // 配置我们需要用到的less、scss全局变量,然后在.vue文件内直接引入
    styleResources: {
        // your settings here
        sass: [],
        scss: ['./assets/vars/*.scss', './assets/abstracts/_mixins.scss'],
        less: ['./assets/vars/*.less'],
        stylus: []
    },
}

万年打野易大师
1.5k 声望1.1k 粉丝