Vue.js 从入坑到入土

Vue.js 从入坑到入土

近年来前端领域发展飞速,从最早的直接写html, js, css 到使用jQuery快速开发,再到近年来使用Vuejs,Reactjs 实现工程化开发,也就短短十几年。虽然前端项目结构越来越复杂,但也越来越规范,清晰,更加工程化。当前较为流行的前端框架有Vuejs、Reactjs、Angularjs 等,而Vuejs由于其灵(zhong)活(wen)轻(wen)便(dang),较为流行。虽然Vuejs有完善的中文文档,但文档主要在介绍其用法,缺少如何实现一个完整项目的指导,本文就介绍如何用Vuejs创建一个完整的前端项目,并与后端进行交互。

本文基于[email protected][email protected]

  1. 所需技术栈:html, javascript, css基础知识,其中js建议了解ES2015标准,特别要掌握promise的使用
  2. 使用的工具:宇宙第一编辑器 VSCode —— 现在对vue项目的支持已经很好了,可以自动安装vue项目需要的插件

0x0 安装

推荐使用yarn代替npm作为包管理器

安装VueCLI

VueCLI是Vuejs官方推出的项目脚手架工具,可以方便的创建、管理Vue项目,还提供优雅的web管理界面

全局安装VueCLI

yarn global add @vue/cli

安装Vue-devtools

Vue-devtools 是vue官方的浏览器开发插件,可以方便的在浏览器中调试vue应用

Vue-devtools 提供多平台的浏览器插件可以直接安装使用

installation

0x1 新建项目

使用 vue create vue_demo 创建新项目,接下来会出现一个交互式命令行帮你配置项目。

注意此处不要使用默认的配置!具体配置参考下图

坑x1 默认的配置十分简单,新手经常不知道怎么写多页面,弄不明白项目怎么组织的,所以需要自定义配置,添加vue-router插件实现多页面跳转

asciicast

0x2 项目框架

此时项目已经创建成功,如果你按照提示启动项目就能看到经典的vue启动页

image

当前的项目结构如下

.                     项目根目录
├── README.md 
├── babel.config.js
├── package.json      项目主配置文件
├── public            公共资源文件夹
│   ├── favicon.ico
│   └── index.html    入口html
├── src               源码根目录
│   ├── App.vue       入口文件
│   ├── assets        资源文件夹
│   │   └── logo.png
│   ├── components    组件文件夹
│   │   └── HelloWorld.vue 
│   ├── main.js       入口配置文件
│   ├── router.js     路由文件
│   ├── store.js      状态管理
│   └── views         页面文件夹
│       ├── About.vue
│       └── Home.vue
└── yarn.lock         依赖文件

在下面的章节具体介绍各个文件、文件夹的用处和如何组织项目。

0x3 项目结构

用vue开发的项目一般是SPA(单页应用),通过动态重写当前页面来与用户交互,而不会重新加载整个新页面。所以要组织多页面就要通过vue-router来组织页面,而vuex用来保存状态。

作为单页应用,当然需要一个入口html文件,也就是 public/index.html :

public/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>vue_demo</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but vue_demo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

这是默认生成的 index.html ,而编译好的文件(网页内容)会被自动注入到 `` 中,也就意味着要使用vue开发的应用,浏览器必须支持js。

而#app所绑定的vue实例,则位于 src/main.js


src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
  router, // 使用vue-router插件
  store, // 使用vuex插件
  render: h => h(App) // 渲染页面
}).$mount('#app') // 绑定到 index.html -> div#app

其中渲染的页面位于 src/App.vue


xxx.vue 是vue中的单文件组件,将 html css javascript 集合在同一个文件,便于组织管理

src/App.vue

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link>
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}
#nav {
  padding: 30px;
}
#nav a {
  font-weight: bold;
  color: #2c3e50;
}
#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

其中 是页面的 `html` 元素,`style` 是样式元素,还可以添加一个 `javascript` 为js元素。其中 是路由组件,会被自动渲染成当前路由对应的页面


src/router.js

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router)
export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/about',
      name: 'about',
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
    }
  ]
})

http://localhost:8080/被渲染成 `home` 页面,`http://localhost:8080/about` 下 被渲染成 about 页面。

技巧 这里加载组件使用函数加载,就可以实现懒加载以优化性能。


src/views/ 则是保存页面文件,例如

src/views/Home.vue

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
  name: 'home',
  components: {
    HelloWorld
  }
}
</script>

这就是路由 home 对应的页面,其中用到了 HelloWorld 组件,需要在js中引入。


src/components/ 里面保存可复用的组件,例如上面的 `` 就是一个独立的组件(component)

src/components/HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>
  </div>
</template>
<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1 {
  margin: 40px 0 0;
}
</style>

这就是一个典型的组件,结构就是一个正常的单文本组件,说明组件就是把一部分页面逻辑抽出来组成一个文件。这样的好处是分离与页面无关的组件,使页面逻辑更加清晰,同时也便于复用组件。如果有以下情况,我建议把部分代码分离成独立的组件:

  1. 需要多次复用的组件,如基础按钮、表单等
  2. 较为复杂但与页面逻辑无关的,如页面的Header、Footer等

技巧 在 style 表情上添加 scoped 属性可以限制CSS生效范围为该组件,不会影响其他元素,在编译时vue解释器会给这些css属性加上随机的hash保证唯一


vue还提供了一个状态管理模式 vuex,可以很方便的管理所有组件的状态。对应文件 src/store.js

src/store.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  }
})

由于我还没用过 vuex 所以自己看文档吧

对于简单的应用也可以不用 vuex ,一个简单的 store 模式 就足够了

0x4 开发指引

有了上面的基础,我们就可以开始写代码了,这里以添加一个 Foo 页面展示从远端获得的数据为例

1. 给页面添加一个 Tab

Tab 属于与页面逻辑无关,但又处处要使用的元素,所以我们将其独立成为一个组件

src/components/Tab.vue

<template>
  <div id="tab">
    <div class="item" @click="router('/')">
      <span>home</span>
    </div>
    <div class="item" @click="router('/about')">
      <span>about</span>
    </div>
    <div class="item" @click="router('/foo')">
      <span>foo</span>
    </div>
  </div>
</template>
<script>
export default {
  name: 'Tab',
  methods: {
    router (url){
      this.$router.push(url)
    }
  }
}
</script>
<style scoped>
#tab {
  display: flex;
  justify-content: space-around;
  align-items: center;
  bottom: 0px;
  position: fixed;
  height: 65px;
  left: 0px;
  width: 100%;
  background-color: aquamarine;
}
.item {
  padding-top: 5px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 50px;
}
</style>

在这里我们创建了一个底部导航栏,有三个 Tab ,并且对应跳转到不同的页面

2. 创建 Foo.vue 相关逻辑并与后端交互

因为我们需要从远端API获取数据并进行展示,就涉及网络请求。在vue中,官方推荐使用 axios 发起请求。axios 是一个基于 promise 的异步网络请求库,相关用法可参考文档或网上教程

安装 axios

yarn add axios

接着在 src/main.js 中加入以下语句启用 axios

import Axios from 'axios'
// 把axios添加到原型链
Vue.prototype.$axios = Axios

现在就可以在vue实例中用 this.$router 来访问路由了


接着来创建 Foo.vue

src/views/Foo.vue

<template>
  <div id="foo">
    <p>{{ result }}</p>
    <button @click="bar()">bar</button>
  </div>
</template>
<script>
export default {
  name: 'Foo',
  data(){
    return {
      result : 'naive'
    }
  },
  methods: {
    bar(){
      /* eslint-disable */
      this.result = 'loading...'
      this.$axios.post('http://httpbin.org/post')
        .then(res => {this.result = res})
        .catch(res => {console.error(res)})
    }
  }
}
</script>
<style>
</style>

具体实现代码里面很清楚了 这个文件的作用就是请求 http://httpbin.org/get 并把结果显示在界面上

注意前后端分离的项目会有跨域(CORS)问题,这个问题比较复杂,巨坑,网上也有较多解决的文章咕咕咕

0x5 技巧及坑们

这里介绍一些我写vue项目遇到的坑和使用的技巧

data methods computed props 的区别

TODO

vue中存储数据有几种方式——data、

0x6 结语

到此,一个简单的vue项目就完成了,对于如何组织项目,前后端如何交互应该已经有了初步了解。本文也会持续更新,欢迎关注我的blog

本文的实例请访问github