新建项目
针对本公司后端开发人员
- 拉取项目后打开
npm installnpm run dev
使用技术
-
vue+vue-router+vue-cli -
使用
eslint、stylelint进行代码规范 -
样式编写使用
css或者less -
可以安装
jsDoc编写js的API文档生成器 -
webpack静态模块打包(通常vue-cli都是使用webpack模板包)
其实唯一改变的就是之前的
.html文件变成了.vue文件,相当于写了一个一个的组件;然后新增了vue-router和webpack模块化工具
项目结构以及规范
目的:方便大家快速定位文件,以及明白新增文件存放位置
-
bulid:webpack相关配置以及脚本文件, 在实际开发过程中主要会用到webpack.base.conf.js,配置less、babel等编译器 -
config:服务器相关配置,常用到config/index.js配置开发环境的 端口号、是否开启热加载 或者 设置生产环境的静态资源相对路径、是否开启gzip压缩、npm run build命令打包生成静态资源的名称和路径等。 -
doc:相关文档,可以认真查阅一遍 -
node_modules:存放install的时候的各种依赖包 -
src:前端源码存放地方,包括项目的源码以及资源,包括页面图片、路由组件、路由配置、入口文件等-
components:组件存放地方;-
app存放业务组件 -
common存放通用组件 -
config组件的配置文件 -
template模板页面(可以直接拷贝进行修改变成自己的组件) -
tools工具类// 函数防抖 import debounce from '@/components/tools/extend/debounce.js'; // 函数节流 import throttle from '@/components/tools/extend/throttle.js'; // 获取兼容ie和非ie的event对象 import getEvent from '@/components/tools/extend/getEvent.js'; // 阻止默认事件 import stopEvent from '@/components/tools/extend/stopEvent.js'; // 绑定|取消事件函数 import event from '@/components/tools/extend/event.js'; // 执行时间记录工具 import fdPerformance from '@/components/tools/extend/performance.js'; // 日志工具 import fdLog from '@/components/tools/extend/log.js'; // console日志模块, 因为console为全局对象,所以用$console为变量名 import $console from '@/components/tools/extend/console.js'; // 判断浏览器的对象 import browser from '@/components/tools/extend/browser.js'; // 处理了的ajax import ajax from '@/components/tools/extend/ajax.js'; // 未处理的axios import axios from 'axios';一个一个的页面我们称为模块,模块是很多组件组成,组件由元件组成
工具类里面是一堆写好的公用方法如上图所示已经全部在
global中引入,global已经在main里面全局引入,所以可以直接使用业务组件的
css全部写到外层css目录components目录下通用组件的
css直接在组件内部建一个index.less,单独编写,方便其他系统复用
-
-
css:样式存放-
animation: 动画样式 (非必须),可以编写公共动画然后引入common:公用类;normalize.less和base.less是重置浏览器的样式,function.less这里全部是功能性命名components: 组件类css, 内部目录结构和组件的文件夹一致config: 配置类css,因为采用less语法,所以可以配置变量 (非必须)mixins: 混入类css,加强css的复用 (非必须),编写通用的方法,可在组件之间通用pages: 页面模块类css, 各个页面的cssunit: 元件类css(非必须)index.less是整个项目的css入口,里面串联各类型的css
-
-
js:js脚本存放-
app:项目依赖的、所有自己写的js放这里config.js项目的配置文件,包含里请求方法,后端服务器地址,日志,调试多个配置项,大家可以在这里配置项目需要的其他项server-config.js此文件为后端接口api的配置文件少了
lib外部引入库,现在外部引入可以直接install安装依赖包,查看现在有哪些外部包可以在package.json里面查询config.js和server-config.js是之前的config.js的拆分,只是把服务端的请求接口拿出来了
-
-
pages:路由用的各个页面, 按照模块划分内部结构,里面存在index.vue作为页面入口 -
images:图片 -
router:路由配置
-
-
static:静态资源,主要放不会更改的文件,比如mock json,需要依赖的外部js、静态图片资源等vue-cli有两个放置静态资源的地方,分别是src/assets文件夹和static文件夹 , assets目录中的文件会被webpack处理解析为模块依赖,只支持相对路径形式static/目录下的文件并不会被Webpack处理:它们会直接被复制到最终的打包目录(默认是dist/static)下。必须使用绝对路径引用这些文件,这是通过在config.js文件中的build.assetsPublicPath和build.assetsSubDirectory连接来确定的 任何放在 static/ 中文件需要以绝对路径的形式引用:/static/[filename]。在这个项目结构中没有assets文件所以不用考虑
-
test:单元测试 -
.eslintrc:eslint 配置eslintrcDoc.js为eslint每项说明文档 -
.stylelintrc:stylelint配置stylelintrcDoc.js为stylintlint每项说明文档 -
.babelrc: babel配置 -
.jsdoc.conf.json:jsdoc文档生成配置文件 -
.gitignore: git 忽略文件配置 -
.eslintignore:eslint验证忽略配置 -
package.json:项目包管理文件 -
index.html:页面入口,经过编译之后的代码将插入到这来
vue-router
单页应用的路径管理器, vue 的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来 ,路由之间的切换就是组件之间的切换;和传统页面的超链接很像
-
实现原理:更新某个指定容器中的内容,而不是重新请求页面
-
两种方式: Hash模式(默认模式)和History模式
-
hash模式:改变#不触发网页重载,常用于锚点定位,会滚动到相应位置,每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置 ,hash 模式的原理是
onhashchange事件(监测hash值变化),可以在 window 对象上监听这个事件 -
History模式: hash模式会在
url中自带# ,想去掉#可以使用history模式//router文件中 export default new Router({ mode: 'history', routes: [{ path: "/", redirect: "/home" },{ path: "/home", name: "home", component:Home }] }) //利用html5 history interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器记录栈,提供了对浏览器历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的URL,但浏览器不会立即向后端发送请求。 //注意:因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404;在服务端增加一个覆盖所有情况的候选资源,如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。 -
路由的使用
npm install vue-router -s //下载 import VueRouter from 'vue-router';//在main.js中引入 Vue.use(VueRouter);//安装插件挂载属性 let router = new VueRouter({routes:[{path:'/home',component:Home}]});//创建路由对象并配置路由规则 router:router//挂载路由到new的vue实例中 <router-view/>//在app.vue中找个地方放组件 -
路由的跳转
- 直接修改地址栏
this.$router.push(‘路由地址’)<router-link to="路由地址"></router-link>
-
路由传参和接收参数
-
使用name传参,接收
$route.name -
通过
router-link标签中的to传参<router-link :to="{name:'home',params:{username:'jspang',id:'555'}}">Hi页面1</router-link> this.$router.push({ name: 'home', params:{username:'jspang',id:'555'}}){{$route.params.username}}-{{$route.params.id}} -
url传参// 路由配置 { path:'/home/:newsId/:newsTitle', component:home }// 传参 <router-link to="/home/198/jspang website is very good">params</router-link>// 接收 {{ $route.params.newsId}} {{ $route.params.newsTitle}} -
query传参<router-link :to="{ path: '/home',query: { queryId: status }}" > router-link跳转Query </router-link> this.$router.push({ path: '/home', query: { queryId: status }});// 接收 this.$route.query.queryId命名路由传递参数需要使用
params来传递, 命名路由这种方式传递的参数,如果在目标页面刷新是会出错的传递参数使用query而且必须配合path来传递参数而不能用name
-
-
$route和$router的区别//$route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。 $route.path //字符串,对应当前路由的路径,总是解析为绝对路径,如 "/home"。 $route.params //一个 key/value 对象,如果没有路由参数,就是一个空对象。 $route.query //一个 key/value 对象,表示URL查询参数。对于路径/home?user=1,$route.query.user为1,如果没有查询参数,则是个空对象。 $route.hash //当前路由的hash值(不带#),如果没有hash值,则为空字符串。 $route.fullPath //完成解析后的 URL,包含查询参数和 hash 的完整路径 $route.matched //数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。 $route.name //当前路径名字$router 是“路由实例”对象,即使用
new VueRouter创建的实例,包括了路由的跳转方法,钩子函数等this.$router.go(-1)//跳转到上一次浏览的页面 this.$router.replace('/home')//指定跳转的地址 this.$router.replace({name:'home'})//指定跳转路由的名字下 this.$router.push('/home')//通过push进行跳转 this.$router.push({name:'home'})//通过push进行跳转路由的名字下使用push方法的跳转会向 history 栈添加一个新的记录,当我们点击浏览器的返回按钮时可以看到之前的页面。
使用replace方法不会向 history 添加新记录,而是替换掉当前的 history 记录,即当replace跳转到的网页后,‘后退’按钮不能查看之前的页面。
-
webpack
vue-cli是构建vue单页应用的脚手架,输入一串指定的命令行从而自动生成vue.js+webpack的项目模板 ,webpack发挥了很大的作用,它使得我们的代码模块化,引入一些插件帮我们完善功能可以将文件打包压缩,图片转base64等;打包之后生成dist文件,这里面只有静态资源和以和一个index.html页面
-
package.json-
package.json来制定名单,需要哪些npm包来参与到项目中来,npm install命令根据这个配置文件增减来管理本地的安装包 ,比如我们安装了jquery,会在这个配置文件中体现{ //从name到private都是在脚手架搭建中输入的项目描述 "name": "default",//项目名称 "version": "1.0.0",//项目版本号 "description": "fd default project ",//项目描述 "author": "yangf",//作者 "private": true,//是否私有,如果私有不会被发布到npm的线上仓库中去 //scripts中的子项即是我们在控制台运行的脚本的缩写npm run key值 = npm run value值 "scripts": { //webpack-dev-server:启动了http服务器,实现实时编译; //inline模式会在webpack.config.js入口配置中新增webpack-dev-server/client?http:localhost:8080/的入口,使得我们访问路径为localhost:8080/index.html "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "unit": "jest --config test/unit/jest.conf.js --coverage", "doc": "jsdoc -r -c .jsdoc.conf.json", "test": "npm run unit", "lint": "eslint --fix --ext .js,.vue src test/unit config build", //使用node运行build文件 "build": "node build/build.js" }, //(项目依赖库):在安装时使用--save则写入到dependencies "dependencies": { "axios": "^0.19.0", "babel-polyfill": "^6.26.0", "vue": "^2.6.0", "vue-router": "^3.0.1" }, //(开发依赖库):在安装时使用--save-dev将写入到devDependencies "devDependencies": { "autoprefixer": "^7.1.2", "babel-core": "^6.22.1" }, //指定node和npm版本 "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" }, //限制了浏览器或者客户端需要什么版本才可运行 "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] }devDependencies里面的插件只用于开发环境,不用于生产环境,即辅助作用,打包的时候需要,打包完成就不需要了。而dependencies是需要发布到生产环境的,自始至终都在。比如webpack等只是在开发中使用的包就写入到devDependencies,而像vue这种项目全程依赖的包要写入到dependencies
-
-
.postcssrc.js- 为兼容所有浏览器,有的
css属性需要对不同的浏览器加上前缀
module.exports = { "plugins": { "postcss-import": {},// 用于@import导入css文件 "postcss-url": {},// 路径引入css文件或node_modules文件 //编辑目标浏览器:使用package.json中的“browserslist”字段 "autoprefixer": {} } } - 为兼容所有浏览器,有的
-
.babelrc是es6解析的配置{ //制定转码规则 "presets": [ //env是使用babel-preset-env插件将js进行转码成es5,设置设置amd,commonjs这样的模块化文件,不进行转码 ["env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } }], "stage-2" ], //使用外部插件来进行转码 "plugins": ["transform-vue-jsx", "transform-runtime"], "env": { "test": {//提前配置的环境变量 "presets": ["env", "stage-2"], "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] } } } -
config文件config内的文件其实是服务于build的,大部分是定义一个变量export出去,通过修改配置文件,让启动和打包项目时根据不同的命令,达到预期的结果dev.env.js
'use strict' //为Webpack设计的合并,提供了一个合并函数,它将数组和合并对象创建一个新对象。 const merge = require('webpack-merge') const prodEnv = require('./prod.env') module.exports = merge(prodEnv, { NODE_ENV: '"development"' }) //https://www.npmjs.com/package/webpack-merge-
prod.env.js当开发时调取
dev.env.js的开发环境配置,发布时调用prod.env.js的生产环境配置 -
index.js
var path = require('path')
module.exports = {
dev: {//开发环境下的配置
assetsSubDirectory: 'static',//子目录
assetsPublicPath: '/',//根目录
proxyTable: {},//可利用该属性解决跨域的问题
host: '127.0.0.1', // 地址
port: 8080, //端口号设置,端口号占用出现问题可在此处修改
autoOpenBrowser: false,//是否在编译后设置自动打开页面
errorOverlay: true,//浏览器错误提示
notifyOnErrors: true,//跨平台错误提示
poll: false, //使用文件系统(file system)获取文件改动的通知devServer.watchOptions
useEslint: true,//使用eslint
showEslintErrorsInOverlay: false,//是否使用eslint全屏报错提示
devtool: 'cheap-module-eval-source-map',//增加调试,该属性为原始源代码(仅限行)不可在生产环境中使用
cacheBusting: true,//使缓存失效
cssSourceMap: true
},
build: {// 生成环境下的配置
index: path.resolve(__dirname, '../dist/index.html'),//index编译后生成的位置和名字
assetsRoot: path.resolve(__dirname, '../dist'),//编译后存放生成环境代码的位置
assetsSubDirectory: 'static',//js,css,images存放文件夹名
assetsPublicPath: './',//发布的根目录,通常为./如果是上线的文件,可根据文件存放位置进行更改路径
productionSourceMap: true,
devtool: '#source-map',// 是否使用 #source-map 开发工具
productionGzip: false,// 是否开启 gzip
productionGzipExtensions: ['js', 'css'],// 需要使用 gzip 压缩的文件扩展名
bundleAnalyzerReport: process.env.npm_config_report//是否使用可视化的分析工具
}
}
//代码压缩后bug定位很困难,引入sourcemap记录压缩前后的位置信息记录,当产生错误时直接定位到未压缩前的位置
-
build
-
build.js构建生产版本,
package.json中的scripts的build就是node build/build.js,输入命令行npm run build对该文件进行编译生成生产环境的代码
-
'use strict'
require('./check-versions')()//调用检查版本的文件
process.env.NODE_ENV = 'production'//设置当前是生产环境
//下面定义常量引入插件
const ora = require('ora')
const rm = require('rimraf')//删除文件
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')//默认读取下面的index.js文件
const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...')
spinner.start()//调用start的方法实现加载动画,优化用户体验
//先删除dist文件再生成新文件,因为有时候会使用hash来命名,删除整个文件可避免冗余
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})
-
check-version.js该文件用于检测node和npm的版本,实现版本依赖 -
utils.js一个用来处理css文件的工具 -
loader.conf.js处理.vue文件,解析这个文件中的每个语言块(template、script、style),转换成js可用的js模块。 -
webpack.base.conf.js是开发和生产共同使用提出来的基础配置文件,主要实现配制入口,配置输出环境,配置模块resolve和插件等 -
development模式下,将侧重于功能调试和优化开发体验,包含如下内容:
- 浏览器调试工具
- 开发阶段的详细错误日志和提示
- 快速和优化的增量构建机制
-
production模式下,将侧重于模块体积优化和线上部署,包含如下内容:
- 开启所有的优化代码
- 更小的bundle大小
- 去除掉只在开发阶段运行的代码
- Scope hoisting和Tree-shaking
- 自动启用
uglifyjs对代码进行压缩
exports/module.exports/export default
-
module.exports对象是由模块系统创建的// 返回一个对象 var app = { name: 'app', version: '1.0.0', sayName: function(name){ console.log(this.name); } } module.exports = app; // 调用 var app = require('./app.js'); app.sayName('hello'); // 返回一个构造函数 var CLASS = function(args){ this.args = args; } module.exports = CLASS; // 调用 var CLASS = require('./CLASS.js'); varc = new CLASS('arguments'); //返回一个实例对象 var CLASS = function(){ this.name = "class"; } CLASS .prototype.func = function(){ alert(this.name); } module.exports = new CLASS(); //调用 var c = require('./CLASS.js'); c.func();//"class"require导出的内容是
module.exports的指向的内存块内容,并不是exports的。区别就是 exports 只是module.exports的引用,辅助后者添加内容用的。用内存指向的方式更好理解。module.exports指向新的对象时,exports 断开了与module.exports的引用,那么通过exports = module.exports让 exports 重新指向module.exports即可。 -
export default 在一个模块里只能有一个,但是export可以有多个
//model.js let e1='export 1'; let e2='export 2'; let e3='export 3'; let e4='export 4'; export {e2}; export {e3}; export {e4}; export default e1; //使用模块的index.js import e1, {e2, e3, e4} from "./model"; console.log(e1); console.log(e2); console.log(e3); console.log(e4); -
模块中通过
export导出的(属性或者方法)可以修改,但是通过export default导出的不可以修改//model.js let e1='export 1'; let e2='export 2'; export {e2}; export default e1; e1='export 1 modified'; e2='export 2 modified'; //index.js import e1, {e2}from "./model"; console.log(e1);//export 1 console.log(e2);//export 2 modified
Node.js认为每个文件都是一个独立的模块。如果你的包有两个文件,假设是“a.js”和“b.js”,然后“b.js”要使用“a.js”的功能,“a.js”必须要通过给 exports 对象增加属性来暴露这些功能: