徒手撸个vue项目框架(下)

栏目: 编程语言 · 发布时间: 4年前

内容简介:写这篇文章的目的,更多是让自己更熟悉vue-cli脚手架创建项目的依据和项目结构,其次是希望我的学习过程可以帮到有疑惑的同学,有什么错误还希望可以得到指教为什么要分上、下,因为最近学习react.js,发现项目框架除了使用的js库不同(vue.js、react.js),配置基本上是大同小异的这也是我占坑(脸大)的理由

写这篇文章的目的,更多是让自己更熟悉vue-cli脚手架创建项目的依据和项目结构,其次是希望我的学习过程可以帮到有疑惑的同学,有什么错误还希望可以得到指教

为什么要分上、下,因为最近学习react.js,发现项目框架除了使用的js库不同(vue.js、react.js),配置基本上是大同小异的

这也是我占坑(脸大)的理由

徒手撸个vue项目框架(下)

徒手撸个react项目框架(上)

徒手撸个react项目框架(下)

这次的目的是接着上篇在框架中添加vue-router,在第三方 工具 包的基础上针对业务进行封装

一、路由

路由是vue组件能够灵活切换的关键所在,vue-router是vue的官方路由。

1.引入vue-router

a.下载

cnpm i vue-router --save-dev
复制代码

b.实例化

我们在src目录下新建router文件夹,并在router新建index.js,同时在components下新建login.vue

import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)

const router = new VueRouter({
  mode: "hash",
  routes: [
    {
      path: "/",
      name: "index",
      components: require("../components/hello.vue")
    },
    {
      path: "/login",
      name: "login",
      components: require("../components/login.vue")
    }
  ]
})

export default router
复制代码

c.挂载

到这里还没结束,我们还需要将router对象挂载到根实例上,在index.js中,如下操作

import router from "./router/index";

new Vue({
  el: "#app",
  router,
  render: h => h(App)
});
复制代码

在你需要渲染组件的地方添加router-view标签

<template>
  <div>
    <router-view />
  </div>
</template>
复制代码

现在你可以在浏览器中访问了

2.利用导航守卫实现登录控制

但是有时候我们的项目要求没有登陆是不能进入首页的,这个功能可以利用router的前置导航守卫实现

beforeEach就是前置导航守卫的钩子函数,它接收三个参数,to、from、next

  • to:即将进入的路由对象
  • from:离开的路由对象
  • next:一定要调用该方法来resolve这个钩子

我使用了h5的localStorage模拟cookie保存用户信息,这里只是个测试,如果你喜欢cookie,可以使用自己喜欢的cookie包,个人喜欢js-cookie

思路是这样的,登录的时候验证完对的用户名和密码,就设置一个字符串作为token存储在本地,token的作用就是下次可以免登录(这种做法的安全性是个问题)

在router/index.js中

router.beforeEach((to, from, next) => {
  // 如果登录的时候设置过
  if(localStorage.getItem("token") != null){
      if(to.name == 'login'){// 如果还访问登录页就导向首页
          next({path: '/'})
      }else{// 给所有其它页面放行
          next()
      }
  }else{// 如果没有设置这个值,为空,说明没有登录,导向登录页
     if (to.name == "login") {
      next();
    } else {
      if (to.name == "login") {
            next(); // 这里要记得给登录页放行,不然会进入死循环
        } else {
            next({ path: "/login" });
        }
    }
  }
})
复制代码

虽然这里已经做得很不错了,但依然没有发挥出vue-router灵活和强大的一面。换句话说,这里能做的事还有很多很多,比如项目是一个管理系统的话,可能会因角色不同,进入首页的侧边栏目是不一样的,虽然看上去是个很复杂的过程,但是仔细分析下也就几步,感兴趣的同学可以看这里

3.利用命名视图实现特殊布局

a.同级路由

同级路由就是两个router-view标签是并列的,分别给两个标签用name属性命名,所以命名视图就是给视图命名了后的视图router-view

// app.vue
<router-view name="navbar"></router-view>
<router-view name="main"></router-view>

// 对应的路由写法就是
import { NavbarComponent, MainComponent } from "@/components"
const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        navbar: NavbarComponent,
        main: MainComponent
      }
    }
  ]
})
// 在app.vue中
<template>
  <div>
    <router-view name="navbar" />
    <router-view name="main" />
  </div>
</template>
复制代码

这时你看到的是这样的

徒手撸个vue项目框架(下)

b.嵌套路由

嵌套路由在管理系统更为常见,我们经常使用的layout布局就是嵌套路由实现的

// 这次我这样定义路由
{
  path: "/index",
  name: "index",
  components: require("@/components/hello"),
  children: [
    {
      path: "login",
      name: "login",
      components: require("@/components/login")
    }
  ]
},

// 并且这样写hello组件
<template>
  <div class="test">index page
    <router-view></router-view>
  </div>
</template>
复制代码

我在浏览器中访问http://localhost:8080/#/index/login,可以访问到下面的页面

徒手撸个vue项目框架(下)

二、与服务端交互

官方推荐与服务端交互使用axios,它是基于ajax封装的,用起来十分简洁

1.下载

cnpm i axios --save-dev
复制代码

2.介绍

axios有两种使用方法一种是使用axios对象调用get或者post请求方法, 另一种是使用axios api,使用axios()函数,参数是个配置对象options

axios.get('/getUser')
    .then(data=>{
        // 请求成功处理函数
        console.log(data)
    })
    .catch(err=>{
        // 请求失败处理函数
        console.log(err)
    })
// 或者
axios({
    url: '/getUser',
    mothod: 'get'
})
    .then(data=>{
        // 请求成功处理函数
        console.log(data)
    })
    .catch(err=>{
        // 请求失败处理函数
        console.log(err)
    })
复制代码

3.封装

在实际的项目中,经常需要对发送的请求或者服务端响应的结果进行处理,请求很多时,挨个处理就很繁琐,也很不切合实际,希望在全局就已经处理好了,我们只负责发送请求和接收服务端响应。

好在axios提供了这种方法。

在src下新建http文件夹,下面新建request.js文件

import axios from "axios";

// 重新实例化一个axios实例,这个实例会覆盖所有默认属性
const server = axios.create({
  baseURL: "/api",
  timeout: 5000,
  heads: { 'content-type': 'application/x-www-form-urlencoded' },
});

// 或者通过修改实例的defaults属性,这两种方法是等价的
server.defaults.baseURL = "/api";
server.defaults.timeout = 5000;

export default server
复制代码

不仅如此,我们还希望在每次发送请求的时候带上登录是设置的token值,在收到服务器错误时可以做出相应的反馈,比如返回的状态码为404,就导航到404页面

这里可以使用axios提供的拦截器对象,具体做法如下

// 设置拦截器
// 请求拦截器
server.interceptors.request.use(
  config => {
    config.headers.token = localStorage.get("token");
    return config;
  },
  err => {
    return Promise.reject(err);
  }
);
// 响应拦截器
server.interceptors.response.use(
  response => {
    return response;
  },
  err => {
  switch (err.response.status) {
      case 404:
        router.push({
          path: "/404"
        });
        break;
      case 504:
        router.push({
          path: "/504"
        });
        break;
    }
    return Promise.reject(err);
  }
);
复制代码

++记得添加错误对应的页面和路由++

// router/index.js
    {
      path: "/404",
      name: "404",
      components: require("../components/404.vue")
    },
    {
      path: "/504",
      name: "504",
      components: require("../components/504.vue")
    }
复制代码

简单的封装已经完成了,只需要在使用的地方引入它

4.跨域代理

这里我们就得说说跨域了,这是前后端分离项目无法避开的。对于前后端分离的项目,之所以跨域是因为浏览器有同源策略的安全限制,来防止跨站请求伪造和跨站脚本攻击

跨域就是违背了同源策略,webpack-dev-server提供了解决跨域的方案

// webpack-dev-server方案
// 在webpack.config.js中devServer
devServer: {
    port: 8080,
    proxy: {
        "/api": {
            target: "http://127.0.0.1:10000", // 代理的目标地址
            changeOrigin: true  // 是否开启跨域
        }
    }
}
复制代码

当然axios也提供了跨域方案,只是比较复杂,还需要后端同学的设置配合才行,这里就不说了

5.使用

这里使用一个例子来说明使用方法。==当然,我没有写登录功能,所以记得把之前写的路由前置守卫的钩子函数注释掉,不然无法跳转==

逻辑是这样的,在接入首页时自动发送请求,获取用户列表,请求方式为get

// hello.vue
import axios from '../http/request.js'

export default {
  created() {
    axios({
        url: '/getUsers',
        method: 'get'
    })
    .then(data =>{console.log(data)})
    .catch(err =>{console.log(err)})
  }
};
复制代码

如果你已经有后端服务器的支持,但是没有写对应的路由,那它会在控制台报错超时,即状态码为504,页面会自动跳转到504页面

假如你和我一样没有后端服务程序,那控制台报错为404,即状态码为404,页面会自动跳转到404页面

三、细节的完善

到这里项目框架功能已经算是撸完了,但是它有很多地方不够正规,和cli创建的项目相比,生产环境和开发环境还没有分离;其次一个合格的大众框架,应该有eslint语法检测;虽然主流的浏览器已经开始支持es6测试语法,但是最好可以加入babel-loader,将es6语法转换es5语法。

1. babel-loader

babel-loader主要是将es6语法转换成浏览器兼容的es5语法,但是项目中node_moudles下也有很多js文件,全都转换会使得速度变慢,并且使项目无法运行,所以需要配置转换文件的路径或者屏蔽掉无需转换的路径

a.下载

babel-loader只是个加载器,转换代码的工作是babel-core babel在转换 ES2015 语法为 ECMAScript 5 的语法时,babel 会需要一些辅助函数, 例如 _extend。babel 默认会将这些辅助函数内联到每一个 js 文件里,这样文件多的时候,项目就会很大。

所以 babel 提供了 transform-runtime 来将这些辅助函数“搬”到一个单独的模块 babel-runtime 中,这样做能减小项目文件的大小

// babel-preset-env这是babel预设的语法规则,babel-preset-是前缀,env是包名
// @babel/plugin-transform-runtime是插件,babel-plugin-是前缀,transform-runtime是插件名

cnpm i babel-core babel-loader babel-preset-env @babel/plugin-transform-runtime --save-dev
复制代码

==这里要注意下载的版本,起初我下载的是babel-plugin-transform-runtime,结果一直报错==

TypeError: this.setDynamic is not a function
复制代码

后来我下载了@babel/plugin-transform-runtime才解决了这个问题

b.配置

// rules中
// include 表示哪些目录中的 .js 文件需要进行 babel-loader
// exclude 表示哪些目录中的 .js 文件不要进行 babel-loader
 module: {
    rules: [
        {
            test: /\.js$/,
            loader: "babel-loader",
            // include: [path.resolve("src")],
            exclude: /node_modules/,
            options: {
                presets: ['env'],  // env提供语法转化的规则,这里是babel预设的
                plugins: ['transform-runtime'] // 这里放我们自己想要使用的插件
            }
        }
    ]
}
复制代码

babel-loader默认是什么都不会做的,需要在预设presets选项中指定插件为你工作的语法规则,babel已经为我们提供了几套预设方案,babel-preset-env就是最新的语法转换规则,其中babel-preset-只是前缀, 另外babel-loader还可以提供丰富的插件做更多的事,只需要在options下的plugins选项中指明

++注意:要用的插件一定要记得下载++

c. 一个例子说明.babelrc文件的用法

下面除了想自动语法转化外还希望将组件懒加载,使加载变快,这时候需要使用插件babel-plugin-syntax-dynamic-import,所以先下载这个包,然后将它放入plugins中

// 下载
cnpm i babel-plugin-syntax-dynamic-import --save-dev

// 在module选项下
rules: [
        {
            test: /\.js$/,
            loader: "babel-loader",
            include: [path.resolve("src")],
            exclude: [path.resolve("node_modules")],
            options: {
                presets: ['env'],  // env提供语法转化的规则,这里是babel预设的
                plugins: ['@babel/transform-runtime',"syntax-dynamic-import"]
            }
        }
    ]
复制代码

假如你需要的插件很多,还需要单独的配置,你可以在项目根目录下创建一个.babelrc的文件替换掉options,配置语法完全同json,下面给个示例,完全等同上面的options

// 在.babelrc文件中,必须是要有两个数组类型的选项:presets、plugins
{
  "presets": ["env"],
  "plugins": ["@babel/transform-runtime", "syntax-dynamic-import"]
}

复制代码

presets更多选项看这里

2.eslint语法检测

eslint对初学者来说是极其不友好的,因为它有严格的要求,使得很多人都想着关闭它,但是不得不说eslint在多人协作团队来说是至关重要的,它可以保证代码风格的一致性

a.下载

cnpm i eslint-plugin-vue eslint-friendly-formatter eslint-loader eslint --save-dev

// 此外记得全局安装eslint支持vue的插件,否则会报错Cannot find module 'eslint-plugin-vue',但是局部仍然要安装
cnpm i eslint-plugin-vue -g
复制代码

b.使用

在webpack配置文件中,添加新的rules

{
        test: /\.(js|vue)$/,
        loader: "eslint-loader",
        include: [path.resolve(__dirname, 'src')], // 指定检查的目录
        options: { // 这里的配置项参数将会被传递到 eslint 的 CLIEngine 
            formatter: require('eslint-friendly-formatter') // 指定错误报告的格式规范
        }
    }
复制代码

c.配置规则

eslint官网提到了三种使用方法(这也是查找规则所在位置的顺序)

  • 使用注释的形式嵌入到代码
  • 在package.json中添加eslintConfig字段,这里指定配置
  • 使用.eslintrc.*文件(任何后缀名)

我选择效仿别人使用.eslintrc.js形式,既然是js文件我们就需要跟写js模块文件一样暴露出一个对象

// 像这样
module.exports = {}
复制代码

这里我只说几个常用的或者重要的配置选项,官网支持中文版,比babel中文版友好很多

// 像这样
module.exports = {
    // 默认情况下,ESLint 会在所有父级目录里寻找配置文件,一直到根目录。如果你想要你所有项目都遵循一个特定的约定时,这将会很有用,但有时候会导致意想不到的结果。为了将 ESLint 限制到一个特定的项目,在你项目根目录下的 package.json 文件或者 .eslintrc.* 文件里的 eslintConfig 字段下设置 "root": true。ESLint 一旦发现配置文件中有 "root": true,它就会停止在父级目录中寻找。
    "root": true,
    // extends: 继承属性,值可以是 "eslint:recommended" "eslint:all" 或者是个插件 或者是个文件
    extends:[
         "eslint:recommended",
         "eslint:all"
        // "plugin:react/recommended"
    ],
    // env: 指定运行的环境,可选值有browser、node、es6等等,值为布尔类型,true为开启,默认为false
    "env": {
        "browser": true
    },
    // plugins: 指定插件,使用之前需下载,vue项目中使用的话vue就是属于插件
    "plugins": [
        "vue"
        // "react"
    ],
    // rules: 指定语法规则,分为0,1,2三个等级对应off(关闭检测),warn(只警告),error(直接报错)
    "rules": {
        "eqeqeq": "off",
        "curly": "error", // if结构中必须使用{}
        "quotes": ["error", "double"]
        // 更多rules看这里 https://www.jianshu.com/p/80267604c775
        // 这里要说下rules的extends特性
        // 假如你的设置是这样的
        // "eqeqeq": ["error", "allow-null"]
        // "eqeqeq": "warn"
        // 那么它最后生成的配置是 "eqeqeq": ["warn", "allow-null"]
    },
    
}
复制代码

4.设置是否开启eslint

优秀的框架肯定会根据配置文件查看是否开启eslint,我在配置文件中定义了一个可配置的变量esLint,当它的值为true时开启eslint语法检测,但默认值为false

const esLint = false

// 替换掉配置好的eslint-loader
....
{
    test: /\.js$/,
    exclude: /node_modules/,
    loader: "babel-loader"
},
esLint?{
    test: /\.(js|vue)$/,
        loader: "eslint-loader",
        include: [path.resolve(__dirname, "src")], // 指定检查的目录
        options: {
          // 这里的配置项参数将会被传递到 eslint 的 CLIEngine
          formatter: require("eslint-friendly-formatter") // 指定错误报告的格式规范
        }
}:{},
...
复制代码

3.使用绝对路径

项目里我一直使用的是相对路径,虽然有友好的代码提示,但是一旦你改变了文件位置,就会报错不止,直到你把所有路径修改正确,最好的做法就是使用绝对路径,并且给路径添加别名,可以方便我们的书写

resolve: {
    alias: {
      ···
      "@": resolve("src")
    }
  }
复制代码

这样我们在import中书写路径时可以用@表示根目录到src,后面继续跟剩下的路径

import hello from "@/components/hello.vue";
复制代码

4.省略后缀名

resolve: {
    extensions:  [".js", ".vue", ".json"], // 当然,这里还可以添加.css、.less、.sass,这都是允许的
    ···
 }
复制代码

现在你在import时有.js或者.vue文件时不用再写后缀名了

import hello from "@/components/hello";
复制代码

结语

花了两天时间终于把框架撸完了,说实话,以前没有在意的细节现在都很通透,当然这对我来说只是一小步,我还打算选择一套UI框架来封装常见的业务,使框架更加的模块化和完善

另外值得一提的是,一段时间前已经发布了@vue/cli3,也就是vue脚手架第三个版本,看了文档发现它只是将插件配置用vue.config.js代替了,它会自动根据vue.config.js的配置生成一份webpack.config.js的文件,我们只需要提供插件,和设置是否开启它

如果你看到这里了,那你的毅力告诉我,你以后技术肯定更加厉害


以上所述就是小编给大家介绍的《徒手撸个vue项目框架(下)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web Design for ROI

Web Design for ROI

Lance Loveday、Sandra Niehaus / New Riders Press / 2007-10-27 / USD 39.99

Your web site is a business--design it like one. Billions of dollars in spending decisions are influenced by web sites. So why aren't businesses laser-focused on designing their sites to maximize thei......一起来看看 《Web Design for ROI》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试