内容简介:写这篇文章的目的,更多是让自己更熟悉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> 复制代码
这时你看到的是这样的
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,可以访问到下面的页面
二、与服务端交互
官方推荐与服务端交互使用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项目框架(下)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Android组件化框架项目详解
- express项目集成mocha测试框架
- 前端项目框架搭建随笔---Webpack踩坑记
- 如何基于Winform开发框架或混合框架基础上进行项目的快速开发
- 前端项目框架搭建随笔---Tab组件的编写
- hapi框架搭建记录(一):初始化项目
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Realm of Racket
Matthias Felleisen、Conrad Barski M.D.、David Van Horn、Eight Students Northeastern University of / No Starch Press / 2013-6-25 / USD 39.95
Racket is the noble descendant of Lisp, a programming language renowned for its elegance and power. But while Racket retains the functional goodness of Lisp that makes programming purists drool, it wa......一起来看看 《Realm of Racket》 这本书的介绍吧!