一、创建Vue项目与npm命令
1. 创建项目
# 先用全局方式安装 npm 的替代工具 cnpm,npm 默认从国外站点下载依赖的js库速度较慢,常常会下载不了;而 cnpm 会从 alibaba 的镜像站点下载依赖的js库速度快
npm install cnpm –g
# 用全局方式安装 vue-cli
npm install -g @vue/cli
# 用全局方式安装 webpack
npm install webpack -g
## 使用webpack模板创建项目:vue-qunar
vue init webpack vue-qunar(项目名称)
2. 从package.json 恢复依赖库
当下载了包含有 package.json 文件的项目时,可在 package.json 所有的目录下执行 npm install 命令从 package.json 中的 dependencies 和 devDependencies 将依赖包安装到当前目录的 ./node_modules 文件夹中。
npm install
3. 常用 npm 命令
# 初始(生成) package.json 文件, 终端会依次询问 name, version, description 等字段
npm init
# 执行默认行为 yes 确认
npm init -y
# 查看依赖树
npm ls
# 安装指定版本的依赖库
npm install JS_LIB_NAME@version
# 安装安装最新版本的依赖库
npm install JS_LIB_NAME
# 删除依赖库
npm uninstall JS_LIB_NAME@version
# 升级依赖库
npm update JS_LIB_NAME
二、项目中用到的第三方插件
1. 安装stylus 样式语言加载器
在项目文件夹下执行npm命令
npm install --save-dev stylus@0.54.8 stylus-loader@3.0.2
build/webpack.base.config 中 resolve节点定义的变量,表示 src/assets/styles
在样式中 import 别的样式要加一个“~”号。
stylus样式语言具有层级结构,每个层级缩进2个字符
css: className { } : ;
2. 安装 fastclick 插件
npm安装时–save 和 --save-dev的区别,前者安装后,在package.json中的dependencies中生成,后者实在devDependencies中生成。
如果是开发中使用的依赖使用–save-dev,如果是编码的依赖使用–save
在移动端H5开发过程中,关于点触可能会遇到如下两个问题:
- 手动点击与真正触发click事件会存在300ms的延迟
- 点击穿透问题(点击行为会穿透元素触发非父子关系元素的事件)
延迟的存在时因为浏览器想知道你是否在进行双击操作;而点击穿透是因为300ms延迟触发时的副作用。而使用fastclick能很好的解决这个问题,增加使用者的体验。
- 安装
npm install fastclick --save
- 在 main.js中使用 fastclick
import fastclick from 'fastclick'
fastclick.attach(document.body)
3. swiper 插件 (轮播图)
https://github.com/surmon-china/vue-awesome-swiper/tree/v2.6.7
- 安装 vue-awesome-swiper
npm install vue-awesome-swiper@2.6.7 --save
- 在main.js中注册(全局注册)
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper'
// require styles
import 'swiper/dist/css/swiper.css'
Vue.use(VueAwesomeSwiper, /* { default global options } */)
4. 安装 babel-polyfill
用于修复浏览器不支持 es6 语法而导致白屏的第三方库
- 安装 babel-polyfill
npm install babel-polyfill@6.26.0 -save
- 在 main.js中使用
import 'babel-polyfill'
5. 安装 axios
Vue.js 2.0 版本推荐使用 axios 来完成 ajax 请求。
Axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中。
Github开源地址: https://github.com/axios/axios
- 具体的使用可参考: http://axios-js.com/
npm install axios
npm install vue-axios@3.2.5
或者两个一起装
npm install --save axios vue-axios
在main.js入口文件中加入
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
在config/index.js中 proxyTable: {}加入
proxyTable: { // 添加请求转发信息,本地开发环境的跨域代理
'/api': {
target: 'http://localhost:8080', // 修改成实际IP: 10.37.129.2,
//changeOrigin: true, // 是否启用跨域
pathRewrite: {
// 在做接口联调时,以 api 开头的请求映射到后端服务器/api 的目录下,但实际上这样写是没有太大意义的,所以可删除 pathRewrite
// '^/api': ''
'^/api': '/static/mock' // /api/china_city_data.json
}
}
在vue中使用
Vue.axios.get(api).then((response) => {
console.log(response.data)
})
this.axios.get(api).then((response) => {
console.log(response.data)
})
this.$http.get(api).then((response) => {
console.log(response.data)
})
6. 引入reset.css
目的:重置页面样式
因为在不同移动端、不同浏览器上页面的初始样式是不一样的,引入reset.css为了保证在每个浏览器上展示出的初始效果是一样的
在main.js导入全局样式, 导入重置页面的默认样式
import 'styles/reset.css'
7.1 引入 border.css
目的:解决移动端1像素边框问题
在main.js中导入 border.css
import 'styles/border.css'
7.2字体图标样式导入
在阿里图标库下载图标,下载解压后选择这几个文件,放入项目中
https://www.iconfont.cn/
在main.js中导入iconfont.css
import 'styles/iconfont/iconfont.css'
7.3 在线图片放在nginx服务器
下载nginx
https://nginx.org/en/download.html
进入到nginx.exe所在的目录
然后在nginx根目录下运行 start nginx
(1)windows 开启nginx,
`bash
start nginx
(2) 结束nginx
```bash
nginx -s stop
在根目录下的操作:
(3)检查配置文件的有效性
nginx -t
(4)处理完当前的请求后关闭
nginx -s quit
(5)修改完配置文件后重新加载
nginx -s reload
(6)打开日志文件
nginx -s reopen
(7) 图片放在www的目录下
8. Vuex 状态管理组件
8.1 安装 Vuex
npm install vuex --save
8.2 使用Vuex
(1) 在src目录下新建store目录,在store目录下新建 index.js,内容如下:
import Vue from 'vue'
import Vuex from 'vuex'
// 状态管理插件
Vue.use(Vuex)
import City from './modules/City'
export default new Vuex.Store({
modules: { // Vuex模块
City // 加载./modules/City.js
}
})
(2)在mian.js入口文件添加
import store from "./store"
在mian.js的new Vue{}里面添加
store,
相当于
'store':store,
例如
new Vue {
el:"#APP",
store,
}
(3) 在store目录下新建 modules 目录,并在目录下新建 City.js,City.js内容如下
import axios from 'axios'
// 驱动应用的数据源
const state = {
hotCities: []
}
/*
Action 类似于 mutation,不同在于:
(1) Action 提交的是 mutation,而不是直接变更状态。
(2) Action 可以包含任意异步操作。
*/
const actions = {
// 后端API的调用
getHotCities ({commit}) {
axios.get('/api/china_city_data.json')
.then((resp) => { // success callback function
const hotCities = resp.data.data.hotCities
// console.log(hotCities);
// commit to mutations
commit('storeHotCities', hotCities)
}, (error) => { // failure callback function
console.error(error)
})
}
}
/*
更改Vuex的store中的状态的唯一方法
*/
const mutations = {
// 将数据存储到Vuex的 state 对象中
storeHotCities (state, hotCities) {
state.hotCities = hotCities
}
}
/*
可以认为是 store 的计算属性,用于获取 state 中的数据。
*/
const getters = {
// getHotCities: (state) => {} // 这样写也可以
hotCities (state) {
return state.hotCities
}
}
export default {
state,
actions,
mutations,
getters
}
(4) 在List.vue 组件中使用
<script>
// import { mapActions,mapGetters,mapMutations,mapState } from 'vuex'
import { mapActions,mapGetters } from 'vuex'
export default {
name: "CityList",
methods: {
// 将Vuex中 actions 的函数映射为组件的 methods 函数
...mapActions(['getHotCities'])
},
mounted() {
this.getHotCities()
},
computed:{
...mapGetters(['hotCities'])
}
}
</script>
8.3 Vuex 实现的过程
1. 需要调用后端API的,Vuex 实现的过程
- 使用
mapActions
辅助函数将组件的 methods 映射为store.dispatch
调用(需要先在根节点注入store
)
methods: {
...mapActions(['ACTION_FUNCTION_NAME', ....])
}
调用可以在 created () { … } 或者 mounted () { … } 中调用 action_function_name。
另一种在组件中调用 Actions 函数的方式是使用 this.$store.dispatch('ACTION_FUNCTION_NAME' [,data])
分发 action。
methods: {
HANDLE_FUNCTION () {
this.$store.dispatch('ACTION_FUNCTION_NAME' [,data])
}
}
-
在 Vuex 中的 actions 通过commit(‘xxx’) 将数据提交给 Mutations 函数处理:
commit(‘MUTATIONS_FUNCTION_NAME’ [ , data] )如下代码所示:
// Vuex 中的 actions
const actions = {
getHotCities ({commit}) {
// 后端API的调用
axios.get('/api/china_city_data.json')
.then((resp) => { // success callback function
const hotCities = resp.data.data.hotCities
// commit to mutations
commit('storeHotCities', hotCities)
}, (error) => { // failure callback function
console.error(error)
})
}
}
另一种是在组件中
this.$store.commit('MUTATION-NAME')
提交 mutation。`
使用 mapMutations
辅助函数将组件中的 methods 映射为 store.commit
调用(需要在根节点注入 store
)
methods: {
HANDLE_FUNCTION () {
// 直接提交 mutation
this.$store.commit('MUTATION_FUNCTION_NAME' [,data])
}
// 将组件中的 methods 映射为 `store.commit` 调用
// ...mapMutations(['xx_fun', 'yy_fun']),
}
- 在 Vuex 中的Mutations 函数将数据存储 到 state 中
MUTATIONS_FUNCTION_NAME (state, data) {
state.recommendList = data.recommendList
state.weekendList = data.weekendList
}
- 在组件中可以通过两种方式获得 Vuex state的的数据
(a) 使用getters的辅助函数 mapGetters 映射为组件的计算属性
computed:{
...mapGetters(['hotCities','cities'])
}
当组件计算属性名和 mapGetters映射的属性名不一样时,可将mapGetters映射的属性名重新指定为组件的计算属性名
computed: mapGetters {
// 计算属性 : mapGetters中返回的属性
computed_prop1: 'hotCities',
computed_prop2: 'xxx'
}
(b) 在组件中使用 $store.getters 访问
this.hotCities = this.$store.getters.hotCities
2. 不需要调用后端API的过程
省略8.1.1 中actions 的部分
使用 mapMutations
辅助函数将组件中的 methods 映射为 store.commit
调用(需要在根节点注入 store
)
methods: {
// 使用方法 1
...mapMutations(['MUTATIONS_FUNCTION_NAME', ...]),
// 使用方法 2
handle_click () {
// 直接提交 mutation
this.$store.commit('MUTATION_FUNCTION_NAME' [,data])
}
}
后继的步骤与前面的一样,这里就不重复说明了。
9. vue-router 中滚动到锚点
<a href="#3"> scroll to div#3 </a>
<div id="3"> ... </div>
//例子,自行对应到你项目的代码
const router = new VueRouter({
routes,
mode: 'history',
scrollBehavior (to, from, savePosition) {
// 如果你的连结是带 # 这种
// to.hash 就会有值(值就是连结)
// 例如:#3
if (to.hash) {
return {
//这个是通过 to.hash 的值来找到对应的元素
// 例如你按下 #3的连结,就会变成 querySelector('#3'), 自然会找到 id=3 的元素
selector: to.hash
}
}
}
})
9.2swagger后端代码生成json格式便于前端调用
swagger的配置已经弄好了 访问http://localhost:8080/doc.html 或者http://localhost:8080/swagger-ui.html即可
10. Better-scroll插件
- 安装 better-scroll
npm install better-scroll@1.15.2 --save
- 卸载better-scroll
npm uninstall better-scroll
- 在组件中使用
<template>
<div class="wrapper" ref="wrapper">
<div class="content">
.....
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
name: "CityList",
data() {
return {
}
},
methods: {
},
mounted() {
this.scroll = new BScroll(this.$refs.wrapper);
//this.scroll = new BScroll('.wrapper'); // 或都指定元素的类样式,或DOM元素
}
}
</script>
11. 去哪儿网打包上线(布暑)
- 下载并安装 nginx-1.16.0.zip服务器(解压即可用户),将nginx解压到 D:\dev 目录下。在nginx目录下执行以下命令测试nginx服务器是否安装成功。
# 进入到nginx-1.16.0目录
cd nginx-1.16.0
# 启动nginx 服务吕
nginx
- 构建 “去哪儿网” 项目。
npm run build
以上命令会在项目中生成一个dist 目录,该即为最终上线的产品。
- 将dist 目录复制到nginx/html 目录中,并将dist 目录改名成qunar。
- 编缉文件 nginx/conf/nginx.conf,修改 server { … } 节点内 location / { … }节点的内容。内容如下:
# 网站的根路径
location / {
# 网站文件所在的路径
root html/qunar;
index index.html index.htm;
}
- 启动nginx 服务器测试布暑的网站。
nginx
- 检查nginx.cong 文件是否配置正确
nginx -t
- 启动nginx 服务器
nginx
- 更改配置后重新加载配置文件,nginx后自动使用新的worker线程处理请求,同时关闭旧的worker线程
nginx -s reload
- 快速停止nginx http服务器
nginx -s stop
- 处理完现在的请求后停止nginx http服务器
nginx -s quit
-
在浏览器地址栏中输入:http://localhost/ 就可能看见我们的网站了。但是目前的网址是localhost 看起不像是个网站的域名。我们也可以配置自己的域名服务器(局域网内)。
-
修改windowns系统中 C:\Windows\System32\drivers\etc\hosts 文件。在文件的末尾添加一个域名的映射。
127.0.0.1 www.mobile.qunar.com
- 在 cmd 命令行中执行命令:ipconfig /flushdns 刷新本地的 DNS 解析服务。
- 在浏览器地址栏中输入:http://www.mobile.qunar.com 就可能看见我们的网站了。
12、 处理跨域请求
-
CORS全称为Cross Origin Resource Sharing(跨域资源共享), 每一个页面需要返回一个名为Access-Control-Allow-Origin的http头来允许外域的站点访问,你可以仅仅暴露有限的资源和有限的外域站点访问。
-
比如说,域名A ( http://domaina.example ) 的某 Web 应用程序中通过< img>标签引入了域名B( http://domainb.foo ) 站点的某图片资源(http://domainb.foo/image.jpg),域名A的那 Web 应用就会导致浏览器发起一个跨站 HTTP 请求。出于安全考虑,浏览器会限制脚本中发起的跨站请求。比如,使用 XMLHttpRequest 对象发起 HTTP 请求就必须遵守同源策略。跨域并非浏览器限制了发起跨站请求,而是跨站请求可以正常发起,但是返回结果被浏览器拦截了。最好的例子是CSRF跨站攻击原理。
-
在当今的 Web 开发中,使用跨站 HTTP 请求加载各类资源(包括CSS、图片、JavaScript 脚本以及其它类资源),已经成为了一种普遍且流行的方式。
-
可以理解为:如果一个请求需要允许跨域访问,则需要在http头中设置Access-Control-Allow-Origin来决定需要允许哪些站点来访问。如假设需要允许https://www.lanqiao.cn这个站点的请求跨域,则可以设置:
Access-Control-Allow-Origin:https://www.lanqiao.cn
例如请求在https://www.lanqiao.cn域名下请求失败的信息如下:
在前后端分离开发的项目中解决跨域请求是常见的问题,在使springboot处理跨域请求时变得非常简单。
2. 解决方案1
这种方案使整个应用都支持跨域。
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//registry.addMapping("/open/**") // 部分接口跨域
registry.addMapping("/**") // 整个应用都支持跨域
.allowedOrigins("*") //允许跨域的域名,可以用*表示允许任何域名使用
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true) //带上cookie信息
.maxAge(3600)
.allowedHeaders("*"); //允许任何请求头
}
}
- 解决方案2
这种方案需每个支持跨域的控制器都添加 @CrossOrigin 注解。(推荐)
@RestController
@CrossOrigin
public class CrossController {
@RequestMapping("/docross")
public Map<String,String> doCross() {
Map<String, String> map = new HashMap<>();
map.put("name", "james");
map.put("age", "22");
return map;
}
}
直接在webstorm中运行 Vue项目时,在 Vue组件中直接请求后端 API 接口,而不需要在nginx 中配置反向代理了。但其缺点也很明显,前端 UI 组件只能请求某一特定的后端 API服务器,不能实现多服务器的负载均衡、高可用、高并发。
import axios from ‘axios’
axios.get(‘http://localhost:8081/docross’) //这里 springboot 运行的端口:8081
.then( res => {
// this.user = res.data
console.log( res.data )
})
.catch( err => {
console.log( err )
})
- 解决方案3 ( Servlet过滤器实现)
过实现Fiter接口在请求中添加一些Header来解决跨域的问题
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Credentials", "true");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN");
if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) {
response.getWriter().println("ok");
return;
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}