美文网首页ITBOX我们就爱程序媛vue.js
Vue教程--使用官方脚手架构建实例(上篇)

Vue教程--使用官方脚手架构建实例(上篇)

作者: SeasonDe | 来源:发表于2017-06-28 15:06 被阅读837次

    本文基于工作项目开发,做的整理笔记
    因工作需要,项目框架由最初的Java/jsp模式,逐渐转移成node/express模式。现在新项目又因为业务流程中的交互操作,会使页面dom根据数据data不断显示变化,便觉得此时是否是最佳实际,应该尝试一下vue框架。因为之前也偷偷的进行了一些vue框架的踩坑工作,已经填了一些坑或者有一些解决方案了,这也才敢用上。依然由后台Java提供API。本文就是做个笔记,说明如何使用Vue官方脚手架开始构建项目。

    前提条件:
    你已经听过、了解过Vue,至少一点点。

    编码环境:
    系统:OS X EI Capitan
    版本:10.12.2

    共2篇:
    上篇:http://www.jianshu.com/p/ee2464501c65(当前)
    下篇:http://www.jianshu.com/p/9baa03eb31fc

    vue.jpg

    目录
    | - 0.传送门
    | - 1.安装
    | - 2.项目初始化
    | - 3.项目构建
      | - Step1.阅读结构
      | - Step2. 阅读代码(泛读)
    | - 4.项目实操
      | - Step1. 修改eslint配置
      | - Step2. 调整结构
      | - Step3. 添加登陆页面
      | - Step4. 模版嵌套
      | - (上下篇分割线)
      | - Step5. 引入mockjs独立开发
      | - Step6. 调用API接口
      | - Step7. 状态管理(store仓库)
      | - Step8. 抽离公共方法
      | - Step9. 剥离组件
      | - Finish. 生成打包
    | - 5.项目部署
      | - a)本地部署
      | - b)服务器部署
    | - 6.结束

    0.传送门

    如果你是直接<script>引用vue.js,去写一些实例或进行页面开发的话,那么你可能去看一官网的一些知识点,以下内容可能不适合你。
    官网‘起步’传送门:http://cn.vuejs.org/v2/guide/#起步

    1.安装

    你已经安装了npm,它是随node.js安装的,装了node.js也就有了它。
    node.js安装下载地址:http://nodejs.cn/download/

    # 检查node.js是否安装,若有则显示版本号
    $ node -v
    # 检查npm的版本号
    $ npm -v
    # 若要更新npm,使用
    $ npm install npm@latest -g
    

    一般,我们还会安装淘宝镜像cnpm,因为在墙内,有时候使用npm安装会很慢,所以需要。

    # 安装cnpm,并指定镜像地址
    $ npm install -g cnpm --registry=https://registry.npm.taobao.org
    

    个人一点点喜好(不介意的话无所谓):
    如果能用npm,我就不太喜欢用cnpm。原因你自己通过npm install / cnpm install安装后,去看看node_modules文件夹内的文件就可以了。

    写文章时,我的目前版本如下:

    Yuxinde-MacBook-Pro:~ yuxin$ node -v
    v6.2.0
    Yuxinde-MacBook-Pro:~ yuxin$ npm -v
    3.8.9
    Yuxinde-MacBook-Pro:~ yuxin$ cnpm -v
    cnpm@4.5.0 (/usr/local/lib/node_modules/cnpm/parse_argv.js)
    npm@3.10.10 (/usr/local/lib/node_modules/cnpm/node_modules/npm/lib/npm.js)
    node@6.2.0 (/usr/local/bin/node)
    npminstall@2.29.1 (/usr/local/lib/node_modules/cnpm/node_modules/npminstall/lib/index.js)
    prefix=/usr/local 
    darwin x64 16.3.0 
    registry=https://registry.npm.taobao.org
    

    vue的安装:

    # 最新稳定版
    $ npm install vue
    # 同时安装它的命令行工具
    $ npm install --global vue-cli
    

    安装好之后我们接下来就使用它的命令行工具vue-cli构建项目。类似的还有在react.js方面,快速上手会使用到‘蚂蚁金服’的Ant-design,使用antd-init或者dva-cli,都是命令行构建项目。

    2.项目初始化

    使用命令行工具,开始一点点构建项目:

    # 去到放项目的文件夹地址
    Yuxinde-MacBook-Pro:~ yuxin$ cd /Users/yuxin/Documents/Season/Project/Vue 
    # 创建基于webpack模版的项目,名称为vue-demo
    Yuxinde-MacBook-Pro:Vue yuxin$ vue init webpack vue-demo
    
      # 提示我有新版本
      A newer version of vue-cli is available.
    
      latest:    2.8.2
      installed: 2.8.1
    
      This will install Vue 2.x version of the template.
    
      # 提示我如果需要使用webpack#1.0的话,创建时用这个命令
      For Vue 1.x use: vue init webpack#1.0 vue-demo
    
    # 填写了一些基础信息,根据你的需要选择
    # 这里我用了ESLint,标准选择了默认的Standard,不熟悉ESLint的话建议不启用或标准选择none,我是给自己挖坑的,玩玩,我不熟。
    # 这里我不启动unit测试和e2e测试
    
    ? Project name vue-demo
    ? Project description This is a demo by season
    ? Author Season <yuxin0721@gmail.com>
    ? Vue build standalone
    ? Install vue-router? Yes
    ? Use ESLint to lint your code? Yes
    ? Pick an ESLint preset Standard
    ? Setup unit tests with Karma + Mocha? No
    ? Setup e2e tests with Nightwatch? No
    
       vue-cli · Generated "vue-demo".
    
       To get started:
       
         cd vue-demo
         npm install
         npm run dev
       
       Documentation can be found at https://vuejs-templates.github.io/webpack
    

    先尝试跑起来,看看项目初始化最初的效果。

    Yuxinde-MacBook-Pro:Vue yuxin$ cd vue-demo
    Yuxinde-MacBook-Pro:vue-demo yuxin$ npm install
    fetchMetadata → network   ▀ ╢███████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░╟
    ^C
    # 安装很慢,所以我control+r停止了安装,尝试使用cnpm install去安装
    Yuxinde-MacBook-Pro:vue-demo yuxin$ cnpm install
    # 此处省略安装信息,安装瞬间完成
    Yuxinde-MacBook-Pro:vue-demo yuxin$ npm run dev
    
    > vue-demo@1.0.0 dev /Users/yuxin/Documents/Season/Project/Vue/vue-demo
    > node build/dev-server.js
    
    > Starting dev server...
    
    
     DONE  Compiled successfully in 3800ms                                  09:06:56
    
    > Listening at http://localhost:8080
    

    浏览器查看,效果如下:

    项目初始化效果.png

    3.项目构建

    接下来,我们就一步步来完善这个项目。但是我想先讨论一个问题,题外话。

    插入一小段题外话:你是如何学习、快速上手,并逐步使用一个框架的?

    我个人的一个小小想法是这个样子的:
    无论是听别人的经验或是自己实践后,都会得到这样一个命题“如果一个东西自己都不会使用,就开始学的话,学会也蛮难的”。其实,也就是说如果不去先用、先去体验,拥有自己的感受、自己的想法、以至于有自己的思考,这样才会得到一个认识,而不是盲目的跟着教程一步一步,一定要思考。

    比如,我想学vue.js,我一定是有一个目的,我想用它做什么,其次才是我怎样去学、去用。我的快速上手,是这样的。

    • 搞清楚我要做什么,我的目的,我的需求;
    • 找到官网,了解知识,这里就是了解vue.js;
    • 找demo,比如说,去玩它的功能,和我的需求去匹配,不满足就找下一个demo,直到对比多个之后“嗯,这个差不多,就先玩下这个”;
    • 几个demo下来,其实你会觉得可能其中某个demo也有你欣赏的功能、欣赏的地方,那留着日后你可能参考、整合、或重写;
    • 确定了demo框架,就开始看它的结构,其实也会大概的看其他demo的结构,有好的地方会搬过来优化这个框架结构,经常是相互借鉴的;
    • 阅读demo的代码,根据我的需求,去编写实现我的功能的代码。遇到问题就爬坑学习,解决问题的经验不断积累,或者又看了其他人的demo,从而不断优化整个架构。
    • 最后就是,玩的差不多的时候。可以从项目初始化创建开始,自己动手搭建一个,加深学习啦。

    Step1. 阅读结构

    初始化的项目结构如下所示:(其中加粗为文件夹,斜体为文件)

    vue-demo
    | - build (运行文件夹,dev/prod运行时需要用到的相关文件,含webpack配置)
    | - config (配置文件夹,比如说dev/prod两种对应的不同接口等设置)
    | - dist (打包生成的部署文件夹,一开始没有的,跑命令npm run build之后可见!!!)
    | - node_modules
    | - src (项目代码)
      | - assets (资源文件夹,为了区分static,这里存自己的资源)
      | - components (页面文件夹,题外:其实我觉得这个像组件,页面应该是views,先保留这样)
      | - router (路由文件夹)
      | - App.vue (vue主文件)
      | - main.js (入口文件,webpack配置的entrance)
    | - static (资源文件夹,为了区分assets,这里存第三方的资源)
    | - .babelrc (转码规则配置)
    | - .editorconfig (代码规范配置)
    | - .eslintignore (eslint检查忽略配置)
    | - .eslintrc.js (eslint配置文件)
    | - .gitignore (git忽略配置)
    | - .postcssrc.js (postcss的load的config,文件里有对应的github地址)
    | - index.html (html主页面)
    | - package.json
    | - README.md

    Step2. 阅读代码(泛读)

    各部分结构已经基本掌握了,部分内容去谷歌了解一下,直接看src文件夹的代码。

    1)index.html

    先看一下index.html里的代码是什么吧。这里唯一需要留意的是下面这两行,告诉我们有一个#app的DOM元素,还有就是会自动插入生成后的JS脚本。

        <div id="app"></div>
        <!-- built files will be auto injected -->
    
    2)main.js

    main.js这个文件里,就做了一件事,声明了一个Vue根实例,主要是说明用APP组件去渲染替换#app的这个DOM元素。

    // The Vue build version to load with the `import` command
    // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
    import Vue from 'vue'
    import App from './App'    // 引入App.vue文件
    import router from './router'    // 引入router文件夹里的index.js(文件夹就是默认找index.js文件,没有报错)
    
    Vue.config.productionTip = false    // 设置为 false 以阻止 vue 在启动时生成生产提示,可以查API文档
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',    // id为app的DOM元素,将被挂载(替换)的元素
      router,    // 引入路由,可以查看router文档
      template: '<App/>',    // 将组件包装成这样一个模版,并且将它挂载(替换)到#app这个DOM上
      components: { App }    // 引入组件,这个组件相当于一个大容器,用于承载我们其他的页面
    })
    
    

    让我们回头看一下DEMO页面,上面明确的提示我们,可以参考的一些有用的网站:
    vue的API文档:http://cn.vuejs.org/v2/api/#全局配置
    vue-router路由:http://router.vuejs.org/zh-cn/
    vue-loader加载器:http://vue-loader.vuejs.org/zh-cn/
    vuex状态管理:http://vuex.vuejs.org/

    3)App.vue

    查看App.vue这个文件,里面主要由3部分组成【template】、【script】、【style】,分别对应的就是页面里的html、js、css。这里唯一留意的就是<router-view></router-view>了,因为在demo页面中,我们看到了一张图片之外,还有下面的文字链接内容,这就要去了解路由了。

    <template>
      <div id="app">
        ![](./assets/logo.png)
        <router-view></router-view>     <!-- 路由的页面将会被嵌入到这里 -->
      </div>
    </template>
    
    <script>
    export default {
      name: 'app'
    }
    </script>
    
    <style>
    #app {
      font-family: 'Avenir', Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    </style>
    
    4)router/index.js

    看一下路由index.js文件,它引入了vue-router路由管理,还引入了一个Hello组件。
    我们需要做的就是将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们。routes数组里就是放着N对映射关系。

    import Vue from 'vue'
    import Router from 'vue-router'
    import Hello from '@/components/Hello'   // 引入Hello组件
    
    Vue.use(Router)
    
    export default new Router({
      routes: [
        /******  一对映射 start  *******/
        {
          path: '/',
          name: 'Hello',
          component: Hello
        }
        /******  一对映射 end  *******/
        // 下一对,如果有的话,别忘了逗号
        // ...
      ]
    })
    
    5)components/Hello.vue

    最后,看一下Hello.vue文件,【template】代码和平时我们写的html基本是一样的,除了用到一些变量、语法,比如这样:

    • {{ msg }} // msg是变量
    • :class="myclass" // myclass是变量
    • :ref="'mass.'+index+'.detail' // index是变量
    • v-if="flag" // flag是变量
    • v-show="page=='Hello'" // page是变量
    • v-for="(item, index) in list" // list是变量
    • v-html="myHtml" // myHtml是变量
    • ...
    <!-- Hello.vue的template部分 -->
    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
        <h2>Essential Links</h2>
        <ul>
          <li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
          <li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
          <li><a href="https://gitter.im/vuejs/vue" target="_blank">Gitter Chat</a></li>
          <li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>
          <br>
          <li><a href="http://vuejs-templates.github.io/webpack/" target="_blank">Docs for This Template</a></li>
        </ul>
        <h2>Ecosystem</h2>
        <ul>
          <li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>
          <li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>
          <li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
          <li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
        </ul>
      </div>
    </template>
    

    那接着看一下【script】,这个项目初始化的例子代码比较简单,只有一个data(name忽视,没什么特别的)。这里其实需要了解很多方法,他们都是用来干什么的,怎么用,什么时候用。我们稍后来看一下。

    <!-- Hello.vue的script部分 -->
    <script>
    export default {
      name: 'hello',
      data () {
        return {
          msg: 'Welcome to Your Vue.js App'
        }
      }
    }
    </script>
    

    那接着看一下【style】,和平时写的没什么区别。这里重点留意下这个关键字scoped,代码这里有一个注释,说“添加这一关键字表示这些样式被限制在这个组件内,不会污染其他组件”。

    <!-- Hello.vue的style部分 -->
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    h1, h2 {
      font-weight: normal;
    }
    
    ul {
      list-style-type: none;
      padding: 0;
    }
    
    li {
      display: inline-block;
      margin: 0 10px;
    }
    
    a {
      color: #42b983;
    }
    </style>
    

    大多时候,基本都会添加scoped关键字,不过比如我们在使用elementUI(或者其他UI)的时候,我们需要覆盖它的样式,那可能需要添加多一个<style></style>,注意这个不带scoped关键字。一般把覆盖的样式独立成一个单独的css文件,通过每个组件的一个个性化class去区分设置多个同样ui的不同样式。

    <!-- element-UI.css UI样式覆盖文件 -->
    <style>
    ...
    </style>
    
    6)常用方法

    先看一下官网的这张“生命周期图示”,正如官网所说,一时看不懂,但是用一用就看懂了,记得回来再看看。

    lifecycle.png

    如果你看得懂这张图,你就会发现App.vue刚好是黄色棱形判断的一种,然后其他components的xxx.vue(比如这里只有一个Hello.vue)就是判断的另外一种。先不多说这张图,在玩的过程中实现你的demo就清楚了。

    我们常用的方法有如下一些:

    • props,页面不会有,但是组件基本都会有,主要用于传数据
    // 举例
    props:{
          id: {
            type: String,
            default: ''
          },
          status: {
            type: Number,
            default: 0
          }
    },
    
    
    • watch,监视页面某一个值的变化,由它变化可以导致其他变化
    // 举例
    watch:{
          status(val) {
            if(val == 1) {
                  this.value = 'aaaa';
            } else {
                  this.value = 'bbbb';
            }
          }
    },
    
    • components,引入组件的时候,我们要在这里声明
    // 举例
    components: {
          AddDialog,    // 组件1
          ContainerFooter    // 组件2
    },
    
    • beforeRouteEnter,在页面进入前要执行的一些操作放在这里,比如请求数据
    // 举例
    beforeRouteEnter (to, from, next) {
          getActData(params).then(response => {
               // 拿回结果后才next()
               ...
               next(vm => {
                    // 赋值
                    ....
               });
          }).catch(err => {
               ....
          });
    },
    
    • created,在页面创建后执行的操作,有些请求也可以放这里
    // 举例
    created() {
          // 加载列表数据
          this.status = this.$route.query.status == undefined ? 1 : this.$route.query.status;
          this.fetchData();
    },
    
    • computed,比如我根据某个变量做的不同显示、判断,都可以变成一个方法放这里,以便调用
    // 举例
    computed: {
          confirmText() {
              return this.edit?'立即修改':'立即创建';
          },
     },
    
    • filters,过滤器,用来将一些值格式化输出显示,比如枚举
    // 举例
    filters: {
            toDateShort(val) {
              return val.substring(0,11);
            }
    },
    
    • methods,整个页面调用的方法,click事件、change事件都在这里
    // 举例
    methods: {
            showLoading() {
              this.loading = true
            },
            hideLoading() {
              this.loading = false
            },
    },
    

    浏览vue.js的api文档https://cn.vuejs.org/v2/api/,还是有好多东西在不断的开发过程中会被用到。比如slot也是一个很值得研究的东西。

    4.项目实操

    接下来,我们就一起实现一个小demo,但它的结构应该是比较完善的,至少可以较好的满足大部分场景和功能需求。

    Step1. 修改eslint配置

    试了一下,standard标准检查出现的错误提示实在太多了,换一下自定义标准,这个没那么严格,不会那么尴尬。

    # 新的配置,用来替换原来的标准
    module.exports = {
      root: true,
      parser: 'babel-eslint',
      "evn": {
        "node": true,
        "commonjs": true,
        "shared-node-browser": true,
        "browser": true
      },
      parserOptions: {
        sourceType: 'module'
      },
      env: {
        browser: true,
        "es6": true,
        "node": true
      },
      // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
      extends: 'eslint:recommended',
      // required to lint *.vue files
      plugins: [
        'html'
      ],
      // add your custom rules here
      'rules': {
        // allow paren-less arrow functions
        'arrow-parens': 0,
        // allow async-await
        'generator-star-spacing': 0,
        // allow debugger during development
        'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
        // allow console
        'no-console': ["error", { allow: ["warn", "error"] }]
      }
    }
    

    Step2. 调整结构

    先调整一下结构吧,因为后面会陆续用到。考虑到后面一个个添加,会没有一个整体直观的印象,感觉有点乱吧,我个人不太喜欢,就先整体了解一下。

    注意:有注释的说明是刚刚新添加的。

    vue-demo
    | - build
    | - config
    | - dist
    | - node_modules
    | - src
      | - api (api接口文件夹)
      | - assets
      | - components (组件文件夹,原来保留)
         | - Hello.vue (转移到views目录下------>)

    | - mock (mock.js假数据接口文件夹)
      | - router
      | - store (仓库文件夹)
      | - styles (样式文件夹)
      | - utils (公共方法文件夹)
      | - vendor (第三方库文件夹)
      | - views (页面文件夹)
         | - dashboard (看板页面)
         | - tablelist (列表页面)
         | - error (错误页面)
         | - layout (模版)
         | - login (登陆页面)
         | - hello (原demo的Hello页面)
            | - Hello.vue (------>转移后的位置)
      | - App.vue
      | - main.js
    | - static
    | - .babelrc
    | - .editorconfig
    | - .eslintignore
    | - .eslintrc.js
    | - .gitignore
    | - .postcssrc.js
    | - index.html
    | - package.json
    | - README.md

    让我们使用npm run dev运行一下demo,会发现报错“在./src/router/index.js里找不到引用的Hello.vue文件”,如下:

    Yuxinde-MacBook-Pro:vue-demo yuxin$ npm run dev
    
    > vue-demo@1.0.0 dev /Users/yuxin/Documents/Season/Project/Vue/vue-demo
    > node build/dev-server.js
    
    > Starting dev server...
    
    
     ERROR  Failed to compile with 1 errors                                             09:49:17
    
    This dependency was not found:
    
    * @/components/Hello in ./src/router/index.js
    
    To install it, you can run: npm install --save @/components/Hello
    > Listening at http://localhost:8080
    
    

    这里你需要改一下引用路径,因为刚刚我们调整过结构。修改之后,发现页面正常了,OK。后面我们不会用到Hello.vue这一块,但暂时保留吧。

    # ./src/router/index.js
    # 将这句
    import Hello from '@/components/Hello'
    # 改为
    import Hello from '@/views/hello/Hello'
    

    Step3. 添加登陆页面

    从这里开始,后面的每一步可能会涉及到使用npm install xxx去安装一些模块,来服务项目的创建、运行。

    安装element-ui,官方网站:http://element.eleme.io/#/zh-CN/component/installation

    Yuxinde-MacBook-Pro:vue-demo yuxin$ npm install element-ui --save
    npm WARN skippingAction Module is inside a symlinked module: not running update node-pre-gyp@0.6.36 node_modules/fsevents/node_modules/node-pre-gyp
    npm WARN skippingAction Module is inside a symlinked module: not running move co@4.6.0 node_modules/ajv-keywords/node_modules/ajv/node_modules/co
    npm WARN skippingAction Module is inside a symlinked module: not running move json-stable-stringify@1.0.1 node_modules/ajv-keywords/node_modules/ajv/node_modules/json-stable-stringify
    npm WARN skippingAction Module is inside a symlinked module: not running add ajv@4.11.8 node_modules/ajv-keywords/node_modules/ajv
    vue-demo@1.0.0 /Users/yuxin/Documents/Season/Project/Vue/vue-demo
    └─┬ element-ui@1.3.7 
      ├── async-validator@1.6.9 
      ├── babel-helper-vue-jsx-merge-props@2.0.2 
      ├── deepmerge@1.4.4 
      └── throttle-debounce@1.0.1 
    
    # 然后把项目跑起来
    Yuxinde-MacBook-Pro:vue-demo yuxin$ npm run dev
    

    ./src/views/login下添加login.vue(也可以index.vue,但是为了清晰,先不这么写)文件,等下编写login.vue的代码。先修改一下路由配置./src/router/index.js,因为等下要访问:

    vue-demo
    | -

    -

    | - src
      | -
      | - views
         | -
         | - login
            | - login.vue (新增)

    -
    # ./src/router/index.js
    import Login from '@/views/login/login'   // 新增
    ...
      routes: [
        {
          path: '/',
          name: 'Hello',
          component: Hello
        },  // 注意逗号
        // 新增
        {
          path: '/login',
          name: 'Login',
          component: Login
        }
      ]
    

    因为我们已经跑起来了项目,在浏览器里访问login看看,http://localhost:8080/#/login 。我们看到一个图片,还发现有个错误,大概说template找不到,先不理它,因为我们的login.vue还是空白的。

    我们注意到这样一个问题,为何路由是/#/login这个样子的,这里你的项目可以继续保持这样,但不喜欢也可以改变它。这里我们改变路由router的模式,由默认的hash变为history
    相关阅读:https://router.vuejs.org/zh-cn/api/options.html#mode

    # ./src/router/index.js
    export default new Router({
      mode: 'history',  // 新增
      routes: [
         ...
    
    

    之后的访问路径就变为http://localhost:8080/login。刚刚我们发现登陆页面出现了一个大大的logo图片,现在把它去掉,修改./src/App.vue文件,如下:

    # ./src/App.vue
    <template>
      <div id="app">
        ![](./assets/logo.png)    // 去掉这一句,删除
        ...
    

    我们将会用到element-ui,去./src/main.js里把它引入进来,如下:

    # ./src/main.js
    ...
    import ElementUI from 'element-ui';    // 新增
    import 'element-ui/lib/theme-default/index.css';    // 新增
    ...
    Vue.use(ElementUI);    // 新增
    
    /* eslint-disable no-new */
    new Vue({
        ...
    

    编辑./src/views/login/login.vue,添加代码如下:

    # ./src/views/login/login.vue
    <template>
      <div class="login-container">
        <el-form autoComplete="on" :model="loginForm" :rules="loginRules" ref="loginForm" label-position="left" label-width="0px" class="card-box login-form">
          <h3 class="title">系统登录</h3>
          <el-form-item prop="email">
            <span class="svg-container"><i class="el-icon-menu"></i></span>
            <el-input name="email" type="text" v-model="loginForm.email" autoComplete="on" placeholder="邮箱"></el-input>
          </el-form-item>
          <el-form-item prop="password">
            <span class="svg-container"><i class="el-icon-setting"></i></span>
            <el-input name="password" type="password" @keyup.enter.native="handleLogin" v-model="loginForm.password" autoComplete="on" placeholder="密码"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" style="width:100%;" :loading="loading" @click.native.prevent="handleLogin">
                登录
            </el-button>
          </el-form-item>
          <div class='tips'>直接点登陆就可以登录成功...</div>
          <div class='tips'>如果想试试验证,建议删除输入框字符试试...</div>
        </el-form>
      </div>
    </template>
    
    <script>
      export default {
        data() {
          // 验证邮箱的规则
          const validateEmail = (rule, value, callback) => {
            if (!this.isEmail(value)) {
              callback(new Error('请输入正确的合法邮箱'));
            } else {
              callback();
            }
          };
          // 验证密码的规则
          const validatePass = (rule, value, callback) => {
            if (value.length < 6) {
              callback(new Error('密码不能小于6位'));
            } else {
              callback();
            }
          };
          return {
            loginForm: {
              email: 'admin@fusio.com.cn',
              password: '123456'
            },
            loginRules: {
              email: [
                  { required: true, trigger: 'blur', validator: validateEmail }
              ],
              password: [
                  { required: true, trigger: 'blur', validator: validatePass }
              ]
            },
            loading: false,
            showDialog: false
          }
        },
        methods: {
          isEmail(str) {
            const reg = /^[a-z0-9](?:[-_.+]?[a-z0-9]+)*@fusio\.com\.cn$/i;
            return reg.test(str.trim());
          },
          handleLogin() {
            // 验证表单
            this.$refs.loginForm.validate(valid => {
              if (valid) {
                // 验证通过
                this.loading = true;
                if(this.loginForm.email == 'admin@fusio.com.cn' && this.loginForm.password == '123456') {
                   // 注意这里有2种方法
                   // 注意下不同写法时this的指向问题,用=>可以保持this的固定指向
    
                   // 方法1
                   // let that = this;
                   // setTimeout(function(){
                   //   that.loading = false;
                   //   that.$router.push({ path: '/' });
                   // }, 1000);
    
                   // 方法2
                   setTimeout(() => {
                      this.loading = false;
                      this.$router.push({ path: '/' });
                   }, 1000)
                        
                }
              } else {
                // 验证失败
                console.error('error submit!!');
                return false;
              }
            });
          }
        }
      }
    </script>
    
    <style rel="stylesheet/scss" lang="scss" scoprd>
      .tips{
        text-align: left;
        font-size: 14px;
        color: #999;
        margin-bottom: 5px;
      }
      .login-container {
        position: relative;
        width: 100%;
        height: 100%;
        height: 100vh;
        background-color: #2d3a4b;
      
        input:-webkit-autofill {
          -webkit-box-shadow: 0 0 0px 1000px #293444 inset !important;
          -webkit-text-fill-color: #fff !important;
        }
        input {
          background: transparent;
          border: 0px;
          -webkit-appearance: none;
          border-radius: 0px;
          padding: 12px 5px 12px 15px;
          color: #eeeeee;
          height: 47px;
        }
        .el-input {
          display: inline-block;
          height: 47px;
          width: 85%;
        }
        .svg-container {
          padding: 6px 5px 6px 15px;
          color: #889aa4;
        }
        .title {
          font-size: 26px;
          font-weight: 400;
          color: #eeeeee;
          margin: 0px auto 40px auto;
          text-align: center;
          font-weight: bold;
        }
        .login-form {
          position: absolute;
          left: 0;
          right: 0;
          width: 400px;
          padding: 35px 35px 15px 35px;
          margin: 120px auto;
        }
        .el-form-item {
          border: 1px solid rgba(255, 255, 255, 0.1);
          background: rgba(0, 0, 0, 0.1);
          border-radius: 5px;
          color: #454545;
        }
        .forget-pwd {
          color: #fff;
        }
      }
    </style>
    

    发现需要调整全局样式,比如body。我们把样式抽离出./src/App.vue,集中到单独的./src/styles/index.scss,注意这里开始用scss,反正都抽出来了。最后在./src/main.js文件中引用。

    vue-demo
    | -
    | - src
      | -
      | - styles
         | - index.scss (新增)
      | -
    | -

    题外话:如果您只是抽成index.css,那就不用执行npm install去安装scss需要的模块。

    # 安装sass需要的模块
    Yuxinde-MacBook-Pro:vue-demo yuxin$ npm install sass-loader --save
    npm WARN skippingAction Module is inside a symlinked module: not running update node-pre-gyp@0.6.36 node_modules/fsevents/node_modules/node-pre-gyp
    npm WARN skippingAction Module is inside a symlinked module: not running move co@4.6.0 node_modules/ajv-keywords/node_modules/ajv/node_modules/co
    npm WARN skippingAction Module is inside a symlinked module: not running move json-stable-stringify@1.0.1 node_modules/ajv-keywords/node_modules/ajv/node_modules/json-stable-stringify
    npm WARN skippingAction Module is inside a symlinked module: not running add ajv@4.11.8 node_modules/ajv-keywords/node_modules/ajv
    vue-demo@1.0.0 /Users/yuxin/Documents/Season/Project/Vue/vue-demo
    ├── UNMET PEER DEPENDENCY node-sass@^4.0.0
    └─┬ sass-loader@6.0.6 
      ├─┬ clone-deep@0.3.0 
      │ ├── for-own@1.0.0 
      │ ├─┬ is-plain-object@2.0.3 
      │ │ └── isobject@3.0.0 
      │ ├── kind-of@3.2.2 
      │ └─┬ shallow-clone@0.1.2 
      │   ├── kind-of@2.0.1 
      │   ├── lazy-cache@0.2.7 
      │   └─┬ mixin-object@2.0.1 
      │     └── for-in@0.1.8 
      ├── lodash.tail@4.1.1 
      └── pify@3.0.0 
    
    npm WARN sass-loader@6.0.6 requires a peer of node-sass@^4.0.0 but none was installed.
    
    # 这里它提示我说node-sass没有安装
    Yuxinde-MacBook-Pro:vue-demo yuxin$ npm install node-sass --save
    (安装过程...略)
    
    # 重新运行项目
    Yuxinde-MacBook-Pro:vue-demo yuxin$ npm run dev
    

    上面我们只是安装了scss用到的模块,然后去查看一下./build/webpack.base.conf.js里的配置代码,你发现它把各种css-loader整合在一个方法放在了./build/utils.js文件里,自己不需要再添加任何配置。

    其他几个文件的代码修改如下:

    # ./src/App.vue
    ...
    // 删除style整块内容
    <style>  
    #app {
      font-family: 'Avenir', Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
    }
    </style>
    ...
    
    # ./src/styles/index.css
    // 全部为添加
    body {
      margin: 0;
    }
    #app {
      font-family: 'Avenir', Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
    }
    
    
    # ./src/main.js
    ...
    import './styles/index.scss';  // 新增
    ...
    

    现在我们访问http://localhost:8080/login,修改输入可以看到表单检查,直接点登录,则登录成功在1s后跳转首页。这里的1s只是为了看一下loading效果。具体代码里的一些vue基础知识,可以查阅官网。

    Step4. 模版嵌套

    前面我们已经能够实现单个无模版页面的编写,现在去看一下模板嵌套吧,这个在项目搭建中还是必须要使用的呢。

    那我们先在./src/views/layout文件夹下创建这几个文件,分别是AppMain.vueindex.jsLayout.vueNavbar.vueSidebar.vue。然后在./src/views/dashboard文件夹下创建dashboard.vue,它将使用Layout.vue模版,这样我们才能看到效果。对应的新增文件结构如下:

    vue-demo
    | -
    | - src
      | -
      | - views
         | -
         | - dashboard
            | - * dashboard.vue* (看板页面,新增)
         | - layout
            | - AppMain.vue (页面容器,新增)
            | - index.js (新增)
            | - Layout.vue (模版框架,新增)
            | - Navbar.vue (顶部导航栏,新增)
            | - Sidebar.vue (侧边菜单栏,新增)
            | - 更多还有面包屑层级路径(略)
      | -
    | -

    我们先不去给上面新建的各个文件添加代码,我们先去修改一下./src/router/index.js文件,创建dashboard页面的路径映射关系,调整一下hello页面的路径映射关系。修改代码如下:

    # ./src/router/index.js 当前的完整代码
    import Vue from 'vue'
    import Router from 'vue-router'
    import Hello from '@/views/hello/Hello'
    import Login from '@/views/login/login'
    import Dashboard from '@/views/dashboard/dashboard'
    
    // Layout引入(这里采用这种写法2,不再提示)
    // import Layout from '../views/layout/Layout';  // 写法1,通过../层级查找
    import Layout from '@/views/layout/Layout';  // 写法2,@默认指向src文件夹,在./build/webpack.base.conf.js的alias可以配置
    
    Vue.use(Router)
    
    export default new Router({
      mode: 'history',
      // 注意
      // routes这里可以好多种结构写法,自己回头研究下吧
      routes: [
        {
          path: '/login',
          name: 'Login',
          component: Login
        },
        {
          path: '/',
          component: Layout,
          redirect: '/dashboard',
          children: [
            { path: '/dashboard', component: Dashboard, name: 'Dashboard' },
            { path: '/hello', component: Hello, name: 'Hello' }
          ]
        },
      ]
    })
    

    这时候,我们去访问http://localhost:8080/dashboard,它提示了我一个之前遇到过的template or render function not defined.错误,由于空白文件引起。现在我们去编辑一下./src/views/dashboard/dashboard.vue和```./src/views/layout/Layout.vue``文件,因为它们现在都是空白的。

    # ./src/views/dashboard/dashboard.vue
    <template>
      <p>This is dashboard page.</p>
    </template>
    
    <script>
      export default {
      }
    </script>
    
    # ./src/views/layout/Layout.vue 临时代码
    # 模版框架
    <template>
      <div class="app-wrapper">
        <p>This is a demo for learning vue.</p>
        <hr/>
        <router-view></router-view>
      </div>
    </template>
    
    <script>
      export default {
      }
    </script>
    

    再次访问http://localhost:8080/dashboard,发现页面正常无报错,并且显示了两个页面的两句话,说明模板框架已经被成功引用。下面我们快速的应用框架代码,借用比较常用的后台框架,学习模版嵌套使用。它们各文件的代码如下所示:

    # ./src/views/layout/Layout.vue 新代码!!!
    # 模版框架
    <template>
      <div class="app-wrapper">
        <div class="navigator-wrapper">
          <div class="navigator-container">
            <div class="logo"></div>
            <Sidebar />
          </div>
        </div>
        <div class="main-container">
          <Navbar />
          <App-main />
        </div>
      </div>
    </template>
    
    <script>
      // 引入组件文件
      import { Navbar, Sidebar, AppMain } from '@/views/layout'; //也就是指向./layout/index.js
      export default {
        // 名称(可不写)
        name: 'layout',
        // 使用组件
        components: {
          Navbar,
          Sidebar,
          AppMain
        }
      }
    </script>
    
    <style rel="stylesheet/scss" lang="scss" scoped>
      .app-wrapper {
        padding-left: 190px;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        &:after {
          content: "";
          display: table;
          clear: both;
        }
        .navigator-wrapper {
          display: block;
          position: absolute;
          z-index: 1000;
          width: 190px;
          left: 0;
          top: 0;
          bottom: 0;
          overflow: hidden;
          background: #2F323A;
          transition: all .28s ease-out;
          &::-webkit-scrollbar-track-piece {
            background: #d3dce6;
          }
          &::-webkit-scrollbar {
            width: 6px;
          }
          &::-webkit-scrollbar-thumb {
            background: #99a9bf;
            border-radius: 20px;
          }
        }
        .navigator-wrapper a:focus, 
        .navigator-wrapper a:active {
          outline: none;
        }
        .navigator-container {
          transition: all .28s ease-out;
        }
        .logo {
          box-shadow: -1px 0 0 1px #000;
          width: 190px;
          height: 70px;
          display: block;
          background-color: #111111;
          background-image: url(../../assets/logo.png);
          background-repeat: no-repeat;
          background-size: contain;
          background-position: 50%;
          overflow: hidden;
        }
        .main-container {
          width: 100%;
          height: 100%;
          overflow-y: hidden;
          transition: all .28s ease-out;
        }
      }
    </style>
    
    # ./src/views/layout/AppMain.vue
    # 页面容器
    <template>
      <section class="app-main" ref="wrap">
        <transition name="fade" mode="out-in">
          <router-view :key="key">
          </router-view>
        </transition>
      </section>
    </template>
    
    <script>
      export default {
        name: 'AppMain',
        computed: {
          key() {
            return this.$route.name !== undefined
              ? this.$route.name + +new Date()
              : this.$route + +new Date()
          }
        },
        methods:{
          scrollBottom() {
            var foot = this.$refs['wrap'].getElementsByClassName('container-footer')[0];
            foot?foot.style.bottom = -this.$refs.wrap.scrollTop + 'px':null;
          }
        }
      }
    </script>
    
    <style rel="stylesheet/scss" lang="scss" scoped>
      .app-main {
        position: relative;
        height: 100%;
        overflow-y: auto;
        background-color: #f5f5f5;
      }
    </style>
    
    # ./src/views/layout/Navbar.vue
    # 顶部导航栏
    <template>
      <div class="navbar">
        <div class="info-container">
          <a href="javascript:;" @click="logout"><i class="el-icon-upload2"></i> 退出</a>
        </div>
      </div>
    </template>
    
    <script>
      export default {
        methods: {
          logout() {
            this.$router.push({ path: '/login' });
          }
        }
      }
    </script>
    <style rel="stylesheet/scss" lang="scss" scoped>
      .navbar {
        transition: height 0.28s;
        position: relative;
        height: 70px;
        line-height: 70px;
        color: #717171;
        background-color: #fff;
        padding: 0 20px 0 30px;
        border-radius: 0px !important;
        box-shadow: 0 0 0 1px #f6f6f6;
        .info-container {
          height: 70px;
          display: inline-block;
          position: absolute;
          right: 32px;
          >a {
            color: #999;
            text-decoration: none;
            &:hover {
              color: #11a0e0;
            }
          }
        }
      }
    </style>
    
    
    # ./src/views/layout/Sidebar.vue
    # 侧边菜单栏
    <template>
      <div class="menu">
        <!-- 为了简单,这里的菜单不是从router读回来的 -->
        <!-- 若要读回来,带上管理权限,请参考文后推荐的demo -->
        <el-menu :default-active="onRoutes" class="el-menu-vertical-demo" unique-opened router theme="dark">
          <el-menu-item index="/dashboard">工作台</el-menu-item>
          <el-menu-item index="/hello">Hello页面</el-menu-item>
        </el-menu>
      </div>
    </template>
    
    <script>
      export default {
        name: 'Sidebar',
        computed: {
          onRoutes () {
            return this.$route.path;
          }
        }
      }
    </script>
    
    <style rel="stylesheet/scss" lang="scss">
      .menu {
        overflow-y: auto;
        height: 100%;
        .el-menu {
          background-color: transparent;
        }
        .el-menu-item {
          background-color: transparent;
          &.is-active {
            background-color: #4e535f;
            color: #fff;
          }
        }
      }
    </style>
    
    # ./src/views/layout/index.js
    # 快速引用
    export { default as Navbar } from './Navbar';
    export { default as Sidebar } from './Sidebar';
    export { default as AppMain } from './AppMain';
    

    再次访问http://localhost:8080/dashboard,发现整个后台的框架基本成型了,点击切换一下dashboardhello页面。上面的文件代码也基本可以看懂的,这一部分就这样了。

    (请继续阅读下篇http://www.jianshu.com/p/9baa03eb31fc)


    学习是一条漫漫长路,每天不求一大步,进步一点点就是好的。

    相关文章

      网友评论

      本文标题:Vue教程--使用官方脚手架构建实例(上篇)

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