美文网首页工作生活
nuxt+element 重零开始搭建项目

nuxt+element 重零开始搭建项目

作者: 黄先森11212 | 来源:发表于2019-07-03 21:57 被阅读0次

一、安装Nuxt

  1. 先安装Node、Npm(至少5.2.0版本),确保安装了npx(npx在NPM版本5.2.0默认安装了)
  2. 进入命令行,输入npx create-nuxt-app <项目名>,比如 npx create-nuxt-app nuxt
  3. 相关选项 image.png
  4. 依赖安装完成 image.png
  5. 安装scsscnpm i -D node-sass sass-loader

二、相关配置

0、配置端口

package.json里面新增

"config": {
        "nuxt": {
            "host": "0.0.0.0",
            "port": "8012"
        }
    }

1、.eslintrc.js

默认:

module.exports = {
    root: true,
    env: {
        browser: true,
        node: true
    },
    parserOptions: {
        parser: 'babel-eslint'
    },
    extends: ['@nuxtjs', 'plugin:nuxt/recommended'],
    // add your custom rules here
    rules: {}
}

只在rules新增以下代码:

rules: {
        'vue/max-attributes-per-line': [
            2,
            {
                singleline: 10,
                multiline: {
                    max: 1,
                    allowFirstLine: false
                }
            }
        ],
        'vue/name-property-casing': ['error', 'PascalCase'],
        'vue/script-indent': ['error', 4, { baseIndent: 1 }],
        'vue/html-indent': ['error', 4, { baseIndent: 1 }],
        'vue/singleline-html-element-content-newline': 0,
        'accessor-pairs': 2,
        'arrow-parens': 'off',
        'arrow-spacing': [
            2,
            {
                before: true,
                after: true
            }
        ],
        'block-spacing': [2, 'always'],
        'brace-style': [
            2,
            '1tbs',
            {
                allowSingleLine: true
            }
        ],
        camelcase: [
            0,
            {
                properties: 'always'
            }
        ],
        'comma-dangle': [2, 'never'],
        'comma-spacing': [
            2,
            {
                before: false,
                after: true
            }
        ],
        'comma-style': [2, 'last'],
        'constructor-super': 2,
        curly: [2, 'multi-line'],
        'dot-location': [2, 'property'],
        'eol-last': 2,
        eqeqeq: [2, 'allow-null'],
        'generator-star-spacing': [
            2,
            {
                before: true,
                after: true
            }
        ],
        'handle-callback-err': [2, '^(err|error)$'],
        indent: [
            0,
            4,
            {
                SwitchCase: 1
            }
        ],
        'jsx-quotes': [2, 'prefer-single'],
        'key-spacing': [
            2,
            {
                beforeColon: false,
                afterColon: true
            }
        ],
        'keyword-spacing': [
            2,
            {
                before: true,
                after: true
            }
        ],
        'new-cap': [
            2,
            {
                newIsCap: true,
                capIsNew: false
            }
        ],
        'new-parens': 2,
        'no-array-constructor': 2,
        'no-caller': 2,
        'no-console': 'off',
        'no-class-assign': 2,
        'no-cond-assign': 2,
        'no-const-assign': 2,
        'no-control-regex': 2,
        'no-delete-var': 2,
        'no-dupe-args': 2,
        'no-dupe-class-members': 2,
        'no-dupe-keys': 2,
        'no-duplicate-case': 2,
        'no-empty-character-class': 2,
        'no-empty-pattern': 2,
        'no-eval': 2,
        'no-ex-assign': 2,
        'no-extend-native': 2,
        'no-extra-bind': 2,
        'no-extra-boolean-cast': 2,
        'no-extra-parens': [2, 'functions'],
        'no-fallthrough': 2,
        'no-floating-decimal': 2,
        'no-func-assign': 2,
        'no-implied-eval': 2,
        'no-inner-declarations': [2, 'functions'],
        'no-invalid-regexp': 2,
        'no-irregular-whitespace': 2,
        'no-iterator': 2,
        'no-label-var': 2,
        'no-labels': [
            2,
            {
                allowLoop: false,
                allowSwitch: false
            }
        ],
        'no-lone-blocks': 2,
        'no-mixed-spaces-and-tabs': 2,
        'no-multi-spaces': 2,
        'no-multi-str': 2,
        'no-multiple-empty-lines': [
            2,
            {
                max: 1
            }
        ],
        'no-native-reassign': 2,
        'no-negated-in-lhs': 2,
        'no-new-object': 2,
        'no-new-require': 2,
        'no-new-symbol': 2,
        'no-new-wrappers': 2,
        'no-obj-calls': 2,
        'no-octal': 2,
        'no-octal-escape': 2,
        'no-path-concat': 2,
        'no-proto': 2,
        'no-redeclare': 2,
        'no-regex-spaces': 2,
        'no-return-assign': [2, 'except-parens'],
        'no-self-assign': 2,
        'no-self-compare': 2,
        'no-sequences': 2,
        'no-shadow-restricted-names': 2,
        'no-spaced-func': 2,
        'no-sparse-arrays': 2,
        'no-this-before-super': 2,
        'no-throw-literal': 2,
        'no-trailing-spaces': 2,
        'no-undef': 2,
        'no-undef-init': 2,
        'no-unexpected-multiline': 2,
        'no-unmodified-loop-condition': 2,
        'no-unneeded-ternary': [
            2,
            {
                defaultAssignment: false
            }
        ],
        'no-unreachable': 2,
        'no-unsafe-finally': 2,
        'no-unused-vars': [
            2,
            {
                vars: 'all',
                args: 'none'
            }
        ],
        'no-useless-call': 2,
        'no-useless-computed-key': 2,
        'no-useless-constructor': 2,
        'no-useless-escape': 0,
        'no-whitespace-before-property': 2,
        'no-with': 2,
        'one-var': [
            2,
            {
                initialized: 'never'
            }
        ],
        'operator-linebreak': [
            2,
            'after',
            {
                overrides: {
                    '?': 'before',
                    ':': 'before'
                }
            }
        ],
        'padded-blocks': [2, 'never'],
        quotes: [
            2,
            'single',
            {
                avoidEscape: true,
                allowTemplateLiterals: true
            }
        ],
        semi: [2, 'never'],
        'semi-spacing': [
            2,
            {
                before: false,
                after: true
            }
        ],
        'space-before-blocks': [2, 'always'],
        'space-before-function-paren': [2, 'never'],
        'space-in-parens': [2, 'never'],
        'space-infix-ops': 2,
        'space-unary-ops': [
            2,
            {
                words: true,
                nonwords: false
            }
        ],
        'spaced-comment': [
            2,
            'always',
            {
                markers: [
                    'global',
                    'globals',
                    'eslint',
                    'eslint-disable',
                    '*package',
                    '!',
                    ','
                ]
            }
        ],
        'template-curly-spacing': [2, 'never'],
        'use-isnan': 2,
        'valid-typeof': 2,
        'wrap-iife': [2, 'any'],
        'yield-star-spacing': [2, 'both'],
        yoda: [2, 'never'],
        'prefer-const': 2,
        'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
        'object-curly-spacing': [
            0,
            'always',
            {
                objectsInObjects: false
            }
        ],
        'array-bracket-spacing': [2, 'never']
    }

2、nuxt.config.js

(1) 引入外部JS

head里面新增script对象,如引入:echarts.jsclipboard.js

head:{
        script: [
            {
                src:
                    'https://cdn.staticfile.org/echarts/4.2.1-rc1/echarts.min.js'
            },
            {
                src:
                    'https://cdn.staticfile.org/clipboard.js/2.0.4/clipboard.min.js'
            }
        ]
}

(2) 路由配置

  1. 新增router对象
 router: {
        // 路由中间件,存放在 `@/middleware/stats.js`
        middleware: 'stats',
        extendRoutes(routes) {
            // 捕获未知路由,然后统一跳转到根路由
            routes.push({
                path: '*',
                redirect: '/'
            })
        },
        scrollBehavior() {
            // 路由跳转,滚动条置顶
            return { x: 0, y: 0 }
        }
    }
  1. middleware/stats.js 说明

在中间件中访问window会报错,因为window是客户端的,中间件将会在服务端、客户端都运行,因此window是不一定存在的!

export default function(context) {
    // 获取userAgent
    context.userAgent = process.server
        ? context.req.headers['user-agent']
        : navigator.userAgent
    const ua = context.userAgent
    // 是否为手机端
    const mobile = /(Android|webOS|iPhone|iPad|iPod|tablet|BlackBerry|Mobile)/i.test(
        ua
    )
    // 是否为微信浏览器
    const wx = /MicroMessenger/i.test(ua)
    // 将值赋给context
    context.isMobile = mobile
    context.isWx = wx
    console.log('中间件,mobile:', mobile)
    console.log('中间件,wx:', wx)
    if (mobile) {
        // 当为手机端,do something

        // redirect:跳转到外部
        context.redirect(302, 'https://m.baidu.com')        
        // $router.push:内部跳转
        context.$router.push('/home')
    }
}

(3) 全局SCSS引入

  1. 安装依赖 cnpm i -S @nuxtjs/style-resourcescnpm i -D node-sass sass-loader
  2. modules 下新增 @nuxtjs/style-resources
modules: [
        // Doc: https://axios.nuxtjs.org/usage
        '@nuxtjs/axios',
        '@nuxtjs/eslint-module',
        // 新增的 @nuxtjs/style-resources
        '@nuxtjs/style-resources'
]
  1. assets下新建style文件夹,然后再新增2个.scss文件:app.scssflex.scss
  2. nuxt.config.js 里新增 styleResources 对象
styleResources: {
        // @ 代表根目录,该值为数组,因此可以继续新增你的 .scss 文件
        scss: ['@/assets/style/app.scss', '@/assets/style/flex.scss']
    }

(4) 打包配置

  1. 打包相关的配置是写在build对象中的,在原有的配置上新增以下
build:{
        // 提取css
        extractCSS: true
}

(5) 环境变量

  1. 安装依赖cnpm i -D cross-env
  2. package.json中的scripts中新增
"scripts": {
        "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
        "precommit": "npm run lint",
        "dev": "nuxt",
        "build": "nuxt build",
        "start": "nuxt start",
        "generate": "nuxt generate",
        // 以下是新增的
        "devprod": "cross-env PATH_TYPE=prod nuxt", // 以正式环境本地运行
        "buildprod": "cross-env PATH_TYPE=prod nuxt build", // 以正式环境打包
        "devprod2": "cross-env PATH_TYPE=prod PATH_HOST=2 nuxt", // 以正式环境、域名为2本地运行
        "buildprod2": "cross-env PATH_TYPE=prod PATH_HOST=2 nuxt build" // 以正式环境、域名为2打包
    }
  1. nuxt.config.js新增env对象
 env: {
        // 环境变量:test、prod
        PATH_TYPE: process.env.PATH_TYPE || 'test',
        // 域名变量:1、2、3.....
        PATH_HOST: process.env.PATH_HOST || '1'
    }

(6) 配置完成的 nuxt.config.js

export default {
    mode: 'universal',
    /*
     ** Headers of the page
     */
    head: {
        title: process.env.npm_package_name || '',
        meta: [
            { charset: 'utf-8' },
            {
                name: 'viewport',
                content: 'width=device-width, initial-scale=1'
            },
            {
                hid: 'description',
                name: 'description',
                content: process.env.npm_package_description || ''
            }
        ],
        link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
        script: [
            {
                src:
                    'https://cdn.staticfile.org/echarts/4.2.1-rc1/echarts.min.js'
            },
            {
                src:
                    'https://cdn.staticfile.org/clipboard.js/2.0.4/clipboard.min.js'
            }
        ]
    },

    /*
     ** router
     */
    router: {
        // 路由中间件
        middleware: 'stats',
        extendRoutes(routes) {
            // 捕获未知路由,然后统一跳转到根路由
            routes.push({
                path: '*',
                redirect: '/'
            })
        },
        scrollBehavior() {
            // 路由跳转,滚动条置顶
            return { x: 0, y: 0 }
        }
    },

    /*
     ** Customize the progress-bar color
     */
    loading: { color: '#fff' },
    /*
     ** Global CSS
     */
    css: ['element-ui/lib/theme-chalk/index.css'],
    /*
     ** Plugins to load before mounting the App
     */
    plugins: ['@/plugins/element-ui'],
    /*
     ** Nuxt.js modules
     */
    modules: [
        // Doc: https://axios.nuxtjs.org/usage
        '@nuxtjs/axios',
        '@nuxtjs/eslint-module',
        // 新增的 @nuxtjs/style-resources
        '@nuxtjs/style-resources'
    ],
    /*
     ** Axios module configuration
     ** See https://axios.nuxtjs.org/options
     */
    axios: {},

    /*
     ** styleResources
     */
    styleResources: {
        scss: ['@/assets/style/app.scss']
    },

    /*
     ** Build configuration
     */
    build: {
        // 提取css
        extractCSS: true,
        transpile: [/^element-ui/],
        /*
         ** You can extend webpack config here
         */
        extend(config, ctx) {}
    },

    /*
     ** env
     */
    env: {
        PATH_TYPE: process.env.PATH_TYPE || 'test',
        PATH_HOST: process.env.PATH_HOST || '1'
    }
}

3、插件处理 plugins

(1) plugins/element-ui.js

原始的:

import Vue from './node_modules/vue'
import Element from './node_modules/element-ui'
import locale from './node_modules/element-ui/lib/locale/lang/en'

Vue.use(Element, { locale })

修改后:

import Vue from 'vue'
import Element from 'element-ui'
// 覆盖默认样式的,(可选)
import '@/assets/style/element-variables.scss'

Vue.use(Element)

assets/style/element-variables.scss 文件说明

/* 改变主题色变量 */
$--color-primary: #c22b46;

/* 改变 icon 字体路径变量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts';

@import '~element-ui/packages/theme-chalk/src/index';

(2) 全局组件注册

  1. 先在plugins下新建global-components.js
import Vue from 'vue'

// 找到components文件夹下以.vue命名的文件
const RC = require.context('@/components/', true, /\.vue$/)

RC.keys().forEach(fileName => {
    const componentConfig = RC(fileName)

    // 因为得到的filename格式是: './baseButton.vue', 所以这里我们去掉头和尾,只保留真正的文件名
    let componentName = fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')
    const index = componentName.indexOf('/')
    if (index !== -1) componentName = componentName.substr(index + 1)

    Vue.component(componentName, componentConfig.default || componentConfig)
})
  1. nuxt.config.jsplugins对象里新增
plugins: [
        '@/plugins/element-ui',
        // 以下统统为新增的
        '@/plugins/global-components'
]

(3) 全局Vue方法、变量

  1. 先在plugins下新建combined-inject.js
import Vue from 'vue'
// 引入接口地址,这只是我个人习惯而已,可选操作
import apiUrl from '@/apiurl'

// ================全局 Vue变量================
// 全局api对象
Vue.prototype.$api = {}

// 全局user对象
Vue.prototype.$user = { name: 'hzq', age: 25 }

// ================全局 Vue方法================
// 深拷贝对象方法
Vue.prototype.$copy = obj => JSON.parse(JSON.stringify(obj))

// 是否为测试环境
const isTest = process.env.PATH_TYPE === 'test'
const Host = process.env.PATH_HOST === '1' ? '***.com' : '***.cn'

// 手机接口域名
const mbHost = isTest ? `http://m.${Host}.com` : `https://m.${Host}.cn`

// 该数组用于装自定义的 Vue.prototype 变量,要去掉 $
const arr = ['api', 'user', 'copy']

export default (c, inject) => {
    // 接口访问地址
    const apiurl = mbHost.replace('//m.', '//www.') + '/api'
    // 封装处理axios
    apiUrl.map(a => {
        const methods = a.methods || 'post'
        Vue.prototype.$api[a.name] = (params = {}, headers = {}) => {
            if (methods === 'get') params = { params }
            return c.$axios[methods](apiurl + a.url, params, {
                headers
            })
        }
    })

    // 通过循环arr,可以同时注入在context,Vue实例中,并且系统会自动将$添加到方法名的前面
    // 然后就可以这样访问:context.app.$user、this.$user
    arr.map(val => inject(val, Vue.prototype['$' + val]))
}
  1. nuxt.config.jsplugins对象里新增
plugins: [
        '@/plugins/element-ui',
        // 以下统统为新增的
        '@/plugins/global-components',
        '@/plugins/combined-inject'
    ]

4、公用CSS处理

可以在@/layouts/default.vue里面的style添加
default.vue

<template>
    <div>
        <nuxt />
    </div>
</template>

<style>
    /* 公共CSS */
    * {
        margin: 0;
        padding: 0;
        font-family: 'Microsoft YaHei', '微软雅黑', 'PingFang SC', Arial;
        -ms-text-size-adjust: 100%;
        -webkit-text-size-adjust: 100%;
        -moz-osx-font-smoothing: grayscale;
        -webkit-font-smoothing: antialiased;
    }
    body {
        background-color: #fff;
    }
    h1,
    h2,
    h3,
    h4,
    h5,
    h6 {
        font-weight: normal;
    }
    ul,
    ol {
        list-style: none;
    }
    a {
        text-decoration: none;
    }
    a:link,
    a:visited,
    a:hover,
    a:active {
        color: #000;
    }
</style>

5、添加Vuex

  1. @/store下新建index.js
export const state = () => ({
    state1: 'state1',
    user: { hzq: { age: 25 } }
})

export const mutations = {
    // 这是一个万能的mutations,它支持vuex的数据流,能实时变化
    // 使用方法如下
    // 拿上面两个state举例:
    // 改变state1:this.$store.commit('save',['state1','newState1'])
    // 改变age:this.$store.commit('save',['user.hzq.age',24])
    save(state, [key, data]) {
        if (!key) throw new Error('mutations save need key!')
        const keyPath = key.split('.')
        const len = keyPath.length
        const lastKey = keyPath.pop()
        let needSave = state
        for (let i = 0; i < len - 1; i++) {
            needSave = needSave[keyPath[i]]
        }
        if (!needSave.hasOwnProperty(lastKey)) {
            throw new Error(`【${key}】 Error Key: ${lastKey}`)
        }
        needSave[lastKey] = data
    }
}
  1. nuxt会自动读取文件,然后生成vuex,就可以在vue里面this.$store.state.user.hzq.age等调用

6、添加百度统计

1.在@/plugins下新建baidu.js

export default ({ app: { router }, store }) => {
    /* 每次路由变更时进行pv统计 */
    router.afterEach((to, from) => {
        try {
            /* 告诉增加一个PV */
            window._hmt = window._hmt || []
            window._hmt.push(['_trackPageview', to.fullPath])
        } catch (e) {}
    })
}
  1. nuxt.config.js里的script新增
script: [
            ***,
            {
                src: 'https://hm.baidu.com/hm.js?*******' // 百度统计js
            }
        ]
  1. nuxt.config.js里的plugins新增
plugins: [
        '@/plugins/element-ui',
        // =============以下统统为新增的=============
        '@/plugins/global-components', // 全局Vue组件注册
        '@/plugins/combined-inject', // 全局Vue变量
        '@/plugins/baidu' // 百度统计
    ]

7、添加百度自动推送

1.在@/plugins下新建baidu_js_push.js

export default ({ app: { router } }) => {
    router.afterEach(() => {
        try {
            const bp = document.createElement('script')
            bp.setAttribute('id', 'baidu_js_push')
            const curProtocol = window.location.protocol.split(':')[0]
            if (curProtocol === 'https') {
                bp.src = 'https://*****/push.js'
            } else {
                bp.src = 'http://*****/push.js'
            }
            const scripts = document.getElementsByTagName('script')
            const links = document.getElementsByTagName('link')
            const s = links[0]
            const curr = [...scripts].find(s => s.id === 'baidu_js_push')
            if (curr) s.parentNode.removeChild(curr)
            s.parentNode.insertBefore(bp, s)
        } catch (e) {}
    })
}
  1. nuxt.config.js里的plugins新增
plugins: [
        '@/plugins/element-ui',
        // =============以下统统为新增的=============
        '@/plugins/global-components', // 全局Vue组件注册
        '@/plugins/combined-inject', // 全局Vue变量
        '@/plugins/baidu', // 百度统计
        '@/plugins/baidu_js_push', // 百度自动推送
    ]

8、添加360自动推送

1.在@/plugins下新建360_js_push.js

export default ({ app: { router } }) => {
    router.afterEach(() => {
        try {
            const isHttp = document.location.protocol === 'http:'
            const scripts = document.getElementsByTagName('script')
            const links = document.getElementsByTagName('link')
            const s = links[0]

            const newwrite = str => {
                const qhres = document.createElement('script')
                qhres.setAttribute('id', 'qhres')
                qhres.setAttribute('charset', 'UTF-8')
                qhres.src = str.split('src="')[1].split('"></script>')[0]
                const curr = [...scripts].find(s => s.id === 'qhres')
                if (curr) s.parentNode.removeChild(curr)
                s.parentNode.insertBefore(qhres, s)
            }
            document.write = newwrite

            const passport = document.createElement('script')
            passport.setAttribute('id', 'sozz')
            passport.setAttribute('charset', 'UTF-8')
            passport.src = isHttp
                ? 'http://****.com/11.0.1.js?****'
                : 'https://****.com/11.0.1.js?****'
            const curr = [...scripts].find(s => s.id === 'sozz')
            if (curr) s.parentNode.removeChild(curr)
            s.parentNode.insertBefore(passport, s)
        } catch (e) {}
    })
}

  1. nuxt.config.js里的plugins新增
plugins: [
        '@/plugins/element-ui',
        // =============以下统统为新增的=============
        '@/plugins/global-components', // 全局Vue组件注册
        '@/plugins/combined-inject', // 全局Vue变量
        '@/plugins/baidu', // 百度统计
        '@/plugins/baidu_js_push', // 百度自动推送
        '@/plugins/360_js_push' // 360自动推送
    ]

三、一些坑点

1、关于Window对象

  1. nuxt的生命周期: nuxt生命周期
    1、红色框:服务端运行
    2、黄色框:服务端&&客户端 都运行
    3、绿色框:客户端运行
    1 中都是不存在window,使用会报错
    2 中可能存在,强行要用时,请用process.client判断
created(){
        if (process.client) {
                window.title = "my nuxt template"
          }
}

3 中存在window

2、组件中无法使用 asyncData、fetch 等

asyncData、fetch等只能在页面级别的.vue使用,即放在@/pages里面的.vue;无法在组件级别的.vue使用,即放在@/components里面的.vue
因此想我们的组件一开始就渲染好的话,可以先在上一个页面或其他页面先获取数据,然后放在vuex里面,这样拿到的组件就是渲染完的。

3、关于asyncData

asyncData方法会在组件(限于页面组件)每次加载之前被调用

asyncData中一些常用的东西(个人理解的)

async asyncData(c) {
        // Vue全局变量
        c.app.$api.Login()
        c.app.$user.hzq.age
        // Vue 路由 c.route 等价于 this.$route
        c.route.path
        // 页面跳转 c.redirect 等价于 this.$router.push
        c.redirect('/home')
        // Vuex c.store 等价于 this.$store
        c.store.commit()
        c.store.state.user
}

四、完结

  1. 这是根据以上配置完成的nuxt项目模板:template_nuxt_element
  2. 这是我实际的nuxt项目:公司官网,官网项目所使用的技术都在这篇文章说明了,应该能满足nuxt项目需求,若有不足希望大家指出。

参考文档:
nuxt官网
Nuxt开发经验分享,让你踩少点坑!

相关文章

网友评论

    本文标题:nuxt+element 重零开始搭建项目

    本文链接:https://www.haomeiwen.com/subject/gdnncctx.html