0%

vue-router传参方式

当发生组件跳转时,组件a 跳转到 组件b,数据的流动是单向的,那么可以考虑使用vue-router的方式传递参数。 vue-router的传参分为3种, 一种是query,一种是params,还有一种动态路由传参比较特殊。 下面来具体总结一下。

query方式传参和接收参数

1
2
3
4
5
6
7
A组件跳转到B组件,通过query传参。
this.$router.push({
name: "PageB",
query: {
id: 2,
content: "这是id为2的content"
}
1
2
3
B组件接收A组件传递过来的参数
this.$route.query.id // 2
this.$route.query.content // "这是id为2的content"

ps: 传参是 this.$router, 通过VueRouter的实例, 接收参数使用的是 this.$route

而且我们发现 url(地址栏)是可以看到 id与content参数的。有点类似于发起get请求时,参数拼接在url中。

params方式传参和接收参数

1
2
3
4
5
6
7
8
A组件跳转到B组件,通过params传参。
this.$router.push({
name: "PageB",
params: {
id: id,
content: "这是params id为2的content"
}
});
1
2
3
B组件接收A组件传递过来的参数
this.$route.params.id // 2
this.$route.params.content // "这是id为2的content"

ps: params传参,push里面只能是 name:’xxxx’,不能是path:’/xxx’,因为params只能用name来引入路由。

params方式传参是不会在url中显示的,如果为了美观 还是选择用params方法进行传参。

动态路由传递参数

一般有种很常见的需求就是做 列表详情页。 一个列表,点击列表中的每一项,进入一个详情页。 而详情页的路由是根据你列表页的点击确定的。每个详情页都有一个对应列表的id,路由是动态的。

1
2
3
4
5
6
7
8
// 文章列表页
<li v-for="article in articles" @click="getDetail(article.id)">

getDetail(id) {
//直接调用$router.push 实现携带参数的跳转
this.$router.push({
path: `/detail/${id}`,
})

这种方法是需要在对应的路由进行配置的。

1
2
3
4
5
6
// router.js
{
path: '/detail/:id',
name: 'Detail',
component: Detail
}
1
2
// detail.vue 文章详情组件
this.$route.params.id

关于刷新问题

其实我们开发大多是单页面组件,我们的所有页面都是一个Vue根实例,所有的data,以及vuex都挂载在根实例上, 如果用户刷新页面,我们保存在页面的数据会全部回到初始值,就是保存的状态全部消失。这样的话如果发生了组件跳转,a页面跳转到b页面 a中传递了数据给b。 我们在b页面中刷新,通过常规方法保存的状态都会直接消失,vue-bus、vuex、props之类的传值方式都会获取不到a传过来的值(所以尽量不要刷新)。

但是vue-router的两种传参方式其实是可以在刷新后获取到 之前的参数的。 我们可以发现刷新后 虽然状态全部没了,但是路由 也可以理解为url在刷新后是不变的。 而通过query方式 与动态路由方式 都会在跳转后 都会在路由中携带参数,所以通过url传参是可以在刷新后保存上一个页面传递过来的参数的。

vue-router打开新页面

vue-router怎么点击打开新的页面,就是a标签里的target=“blank

vue本身开发的就是单页面组件,路由跳转其实在浏览器看来并没有跳转(或者说并没有打开页面,本质上还是在一个index.html上)

1
<router-link target="_blank"></router-link>

编程式导航

使用this.$router.resolve

1
2
3
4
5
6
7
seeShare(){
let routeUrl = this.$router.resolve({
path: "/share",
query: {id:96}
});
window.open(routeUrl.href, '_blank');
}

如果用这种方法进行参数传递 其实是不能通过params方式进行参数传递的,只能通过query进行传递。其实想想也可以理解,因为_blank打开了一个新的页面,两个html页面之间进行数据传递,最好用的就是url进行传参。

这是一篇随笔,记录我的一些人生经历,方便以后自我回顾,以及给我们工作室的学弟学妹一些关于找工作的信息及建议,方便他们少走弯路。

Read more »

最近突然想看看自己对一个项目的贡献,打算总结常见的git统计命令,可以说对以前自己项目的一个回顾吧

Read more »

这篇文章主要总结了js中对象的创建方法 字面量创建、工厂模式、构造函数模式、原型模式、以及组合模式,以及js中继承的实现 原型链、经典继承、组合继承、拷贝继承、原型式继承、组合继承、ES6的extends继承等

Read more »

本文根据经验总结了一些对于webpack的优化方法。分别从webpack打包前的构建优化(提高代码构建速度)。打包后的线上体验优化。参考并总结了现在常见的优化方法。希望以后可以提升webpack构建的项目性能。

提高构建速度

减小编译范围

缩小编译范围,减少不必要的编译工作,即 modules、mainFields、noParse、includes、exclude、alias全部用起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const resolve = dir => path.join(__dirname, '..', dir);

// ...
resolve: {
modules: [ // 指定以下目录寻找第三方模块,避免webpack往父级目录递归搜索
resolve('src'),
resolve('node_modules'),
resolve(config.common.layoutPath)
],
mainFields: ['main'], // 只采用main字段作为入口文件描述字段,减少搜索步骤
alias: {
vue$: "vue/dist/vue.common",
"@": resolve("src") // 缓存src目录为@符号,避免重复寻址
}
},
module: {
noParse: /jquery|lodash/, // 忽略未采用模块化的文件,因此jquery或lodash将不会被下面的loaders解析
// noParse: function(content) {
// return /jquery|lodash/.test(content)
// },
rules: [
{
test: /\.js$/,
include: [ // 表示只解析以下目录,减少loader处理范围
resolve("src"),
resolve(config.common.layoutPath)
],
exclude: file => /test/.test(file), // 排除test目录文件
loader: "happypack/loader?id=happy-babel" // 后面会介绍
},
]
}

并发构建

terser-webpack-plugin

压缩是构建中耗时占比较大的一环,我们可以启用 terser-webpack-plugin 的多线程压缩,减少压缩时间。

1
2
3
4
5
6
7
8
9
module.exports = {
optimization: {
minimizer: [
new TerserJSPlugin({
parallel: true // 开启多线程压缩
})
]
}
}

webpack-parallel-uglify-plugin

实际上,搭载 webpack-parallel-uglify-plugin 插件,这个过程可以倍速提升。我们都知道 node 是单线程的,但node能够fork子进程,基于此,webpack-parallel-uglify-plugin 能够把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程,从而实现并发编译,进而大幅提升js压缩速度,如下是配置。多线程压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');

// ...
optimization: {
minimizer: [
new ParallelUglifyPlugin({ // 多进程压缩
cacheDir: '.cache/',
uglifyJS: {
output: {
comments: false,
beautify: false
},
compress: {
wa rnings: false,
drop_console: true,
collapse_vars: true,
reduce_vars: true
}
}
}),
]
}

HappyPack

同 webpack-parallel-uglify-plugin 插件一样,HappyPack 也能实现并发编译,从而可以大幅提升 loader 的解析速度, 如下是部分配置。

1
2
3
4
5
6
7
8
9
const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const createHappyPlugin = (id, loaders) => new HappyPack({
id: id,
loaders: loaders,
threadPool: happyThreadPool,
verbose: process.env.HAPPY_VERBOSE === '1' // make happy more verbose with HAPPY_VERBOSE=1
});
12345678

那么,对于前面 loader: "happypack/loader?id=happy-babel" 这句,便需要在 plugins 中创建一个 happy-babel 的插件实例。

1
2
3
4
5
6
7
8
9
plugins: [
createHappyPlugin('happy-babel', [{
loader: 'babel-loader',
options: {
babelrc: true,
cacheDirectory: true // 启用缓存
}
}])
]

像 vue-loader、css-loader 都支持 happyPack 加速,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
plugins: [
createHappyPlugin('happy-css', ['css-loader', 'vue-style-loader']),
new HappyPack({
loaders: [{
path: 'vue-loader',
query: {
loaders: {
scss: 'vue-style-loader!css-loader!postcss-loader!sass-loader?indentedSyntax'
}
}
}]
})
]

noParse(无需解析内部依赖的包)

对于我们引入的一些第三方包,比如jQuery,在这些包内部是肯定不会依赖别的包,所以根本不需要webpack去解析它内部的依赖关系,可以在webpack配置文件中的module属性下加上noParse属性,它的值是一个正则表达式,用来匹配无需解析的模块,这样可以节约webpack的打包时间,提高打包效率。

1
2
3
module:{
noParse:/jquery/
}

DllPlugin & DllReferencePlugin 提前打包公共依赖

我们都知道,webpack打包时,有一些框架代码是基本不变的,比如说 babel-polyfill、vue、vue-router、vuex、axios、element-ui、fastclick 等,这些模块也有不小的 size,每次编译都要加载一遍,比较费时费力。使用 DLLPlugin 和 DLLReferencePlugin 插件,便可以将这些模块提前打包。

当项目达到一定体量,打包速度、热加载性能优化的需求就会被提出来,毕竟谁也不愿意修改后花上十几秒甚至几分钟等待修改视图更新。接下里我会介绍一些通用的优化策略,但需要注意的是,项目本身不能去踩一些无法优化的坑,已知两坑:超多页( html-webpack-plugin 热更新时更新所有页面)和动态加载未指明明确路径(打包目录下所有页面)。

DllPlugin 和 DllReferencePlugin 绝对是优化打包速度的最佳利器,它可以把部分公共依赖提前打包好,在之后的打包中就不再打包这些依赖而是直接取用已经打包好的代码,通常情况能降低 20% ~ 40% 打包时间,当然它也有缺点:

  • 需要在初始化和相关依赖更新时,额外执行一条命令
  • 通常 dll 是在 .html 文件中引入,滥用会导致首屏加载变慢

但总归来说是利大于弊。

为了完成 dll 过程,我们需要准备一份新的webpack配置,即 webpack.dll.config.js。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const webpack = require("webpack");
const path = require('path');
const CleanWebpackPlugin = require("clean-webpack-plugin");
const dllPath = path.resolve(__dirname, "../src/assets/dll"); // dll文件存放的目录

module.exports = {
entry: {
// 把 vue 相关模块的放到一个单独的动态链接库
vue: ["babel-polyfill", "fastclick", "vue", "vue-router", "vuex", "axios", "element-ui"]
},
output: {
filename: "[name]-[hash].dll.js", // 生成vue.dll.js
path: dllPath,
library: "_dll_[name]"
},
plugins: [
new CleanWebpackPlugin(["*.js"], { // 清除之前的dll文件
root: dllPath,
}),
new webpack.DllPlugin({
name: "_dll_[name]",
// manifest.json 描述动态链接库包含了哪些内容
path: path.join(__dirname, "./", "[name].dll.manifest.json")
}),
],
};
1234567891011121314151617181920212223242526

接着, 需要在 package.json 中新增 dll 命令。

1
2
3
4
"scripts": {
"dll": "webpack --mode production --config build/webpack.dll.config.js"
}
123

运行 npm run dll 后,会生成 ./src/assets/dll/vue.dll-[hash].js 公共js 和 ./build/vue.dll.manifest.json 资源说明文件,至此 dll 准备工作完成,接下来在 webpack 中引用即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
externals: {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'vuex',
'elemenct-ui': 'ELEMENT',
'axios': 'axios',
'fastclick': 'FastClick'
},
plugins: [
...(config.common.needDll ? [
new webpack.DllReferencePlugin({
manifest: require("./vue.dll.manifest.json")
})
] : [])
]
123456789101112131415

dll 公共js轻易不会变化,假如在将来真的发生了更新,那么新的dll文件名便需要加上新的hash,从而避免浏览器缓存老的文件,造成执行出错。由于 hash 的不确定性,我们在 html 入口文件中没办法指定一个固定链接的 script 脚本,刚好,add-asset-html-webpack-plugin 插件可以帮我们自动引入 dll 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const autoAddDllRes = () => {
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
return new AddAssetHtmlPlugin([{ // 往html中注入dll js
publicPath: config.common.publicPath + "dll/", // 注入到html中的路径
outputPath: "dll", // 最终输出的目录
filepath: resolve("src/assets/dll/*.js"),
includeSourcemap: false,
typeOfAsset: "js" // options js、css; default js
}]);
};

// ...
plugins: [
...(config.common.needDll ? [autoAddDllRes()] : [])
]

生成合理的 Source Map

在 webpack 4 中,是否生成 Source Map 以及生成怎样的 Source Map 是由 devtool 配置控制的,选择合理的 Source Map 可以有效的缩短打包时间。在选择前我们还是应该明白,不设置 Source Map 时打包是最快的,之所以需要 Source Map ,是因为打包后的代码结构、文件名和打包前完全不一致,当存在报错时我们只能直接定位到打包后的某个文件,无法定位到源文件,极大程度增加了调试难度。而 Source Map 就是为了增强打包后代码的可调试性而存在的,所以我们在开发环境总是需要它,在生产环境则有更多选择。

devtool 可选配置有 noneevalcheap-eval-source-map 等 13 种,各自功能和性能比较在 文档 中有详细介绍。

配置项由一个或多个单词和连字符组成,每个单词都有其含义和性能损耗,每个配置项最终意义就由这些单词决定:

  • none 不生成 Source Map ,性能 +++
  • eavl 每个模块由 eval 执行,不能正确显示行数,不能用生产模式,性能 +++
  • module 报错显示原始代码,性能 -
  • source 报错显示行列信息,显示 babel 转译后代码,性能 –
  • cheap 低开销模式,不映射列,性能 +
  • inline 不生成单独的 Source Map 文件,性能 o

开发环境

由于开发模式建议显示报错源码和行信息,所以 modulesource 都是需要的,为了性能我们又需要 evalcheap ,所以参照配置项能找到最适合开发环境的配置是 devtool: cheap-module-eval-source-map

生产环境

生产环境由于几乎不存在调试需求( JS 相关调试),所以建议大家设置 devtool: none ,在需要调试的时候再更改设置为 devtool: cheap-module-source-map

分别配置 mode属性,设置为 development 将获得最好的开发体验,设置为 production 将专注项目编译部署,比如说开启 Scope hoisting 和 Tree-shaking 功能。

线上环境用户体验优化

路由懒加载

Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。

路由懒加载:

1
2
3
4
5
6
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})

组件异步加载

如果组件在页面加载时不需要,只在调用时用到,这时可以使用异步组件的写法。仅仅是引入和组件注册写法不同

1
2
3
4
5
6
7
8
9
10
11
12
// template
<test v-if="showTest"></test>

// script
components: {
test: () => import('./test') // 将组件异步引入,告诉webpack,将该部分代码分割打包
},
methods:{
clickTest () {
this.showTest = !this.showTest
}
}

魔法注释

在懒加载的同时可以使用魔法注释:Prefetching,可以在首页资源加载完毕后,空闲时间时,将动态导入的资源加载进来,这样即可以提高页面加载速度又保证了用户体验。(感觉有点类似于h2的主动推送)

1
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')

代码优化 压缩

vue-cli已经使用UglifyJsPlugin 插件来压缩代码,可以设置成如下配置:

1
2
3
4
5
6
7
8
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
drop_console: true,
pure_funcs: ['console.log']
},
sourceMap: false
})

代码分离(SplitChunksPlugin)

代码分离有两个优点:

  • 剥离公共代码和依赖,避免重复打包

  • 避免单个文件体积过大

    总加载体积一致,浏览器加载多个文件通常快于单个文件

为了解决公共资源被重复打包问题,我们就需要 SplitChunksPlugin 的帮助,它可以把代码分离成不同的 bundle ,在页面需要时被加载。另外 SplitChunksPlugin 是 webpack 4 的内置插件,所以我们不需要去独立安装它。

使用方法:只需在主配置文件中添加如下配置即可

1
2
3
4
5
Optimization: {
splitChunks: {
Chunks: 'all'
}
}

tree shaking

上面我们分离代码,解决了项目中部分代码被重复打包到多个生成物中的问题,有效地缩小了生成物体积,但其实我们还可以在此基础上进一步缩小体积,这就涉及本小节的概念 tree shaking 。

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 import 和 export。

你可以将应用程序想象成一棵树。绿色表示实际用到的 source code(源码) 和 library(库),是树上活的树叶。灰色表示未引用代码,是秋天树上枯萎的树叶。为了除去死去的树叶,你必须摇动这棵树,使它们落下。

我们回头看在使用 SplitChunksPlugin 时生成的文件,可以发现 say 函数没有使用但是却被打包进来了,它实际上是无用的代码,也就是文档中说的 dead-code 。要删除这些代码,只需要把 mode 修改为 production (让 tree shaking 生效),再次打包~

不过需要注意的是, tree shaking 能移除无用代码的同时,也有一定的副作用(错误识别无用代码)。比如你可能会遇到 UI 组件库没有样式的问题,这个问题原因在于 tree shaking 不仅对 JS 生效,也对 CSS 生效 。我们通常在导入 CSS 时使用 import 'xxx.min.css' , ES6 的静态导入 + 生产环境满足了 tree shaking 的生效条件,并且 Webpack 无法判断 CSS 有效,所以它被当做了 dead-code 然后被删除。为了解决这个问题,你可以在 package.json 中添加一个 sideEffects 选项,告知 Webpack 那些文件是可以直接引入而不用 tree shaking 检查的,使用如下:

1
2
3
4
5
6
7
package.json
{
"sideEffects": [
"*.css",
"*.styl(us)?"
]
}

cdn加速

浏览器从服务器上下载 CSS、js 和图片等文件时都要和服务器连接,而大部分服务器的带宽有限,如果超过限制,网页就半天反应不过来。而 CDN 可以通过不同的域名来加载文件,从而使下载文件的并发连接数大大增加,且CDN 具有更好的可用性,更低的网络延迟和丢包率 。

或者直接开启全站加速服务。对你部署的域名或者ip进行加速,无需对服务器源站上的资源进行改造,全站加速会智能区分动静态内容并分别加速。整个网站的资源访问速度都会大大加快。

参考博客

webpack优化的一些基本方法

使用webpack4提升180%编译速度

三十分钟掌握Webpack性能优化

项目创建、数据埋点、监控埋点、代码规范、工作流、项目进度、上线部署、文档、项目存档(语雀)、监控、数据分析、交接。

恭喜你!如果你能独立负责一个项目,证明你已经有独挡一面的能力了。接下来请仔细阅读该文档。做好独立负责一个项目的准备吧!

前置知识,秘钥配置

你需要把你的秘钥,上传到我们的gitlab上。

1571650392063

ssh配置参考教程

项目新建

我们工作室所有的项目基本上都是保存在我们自己内部的gitlab上的。https://git.ncuos.com ps: 你需要被邀请加入项目组

创建项目在projects里点击新建new project后将会出现以下界面。

1571382102230

1571381776046

一般项目都会选择在dev组里新建,这样你的代码方便所有人进行查看。但是,因为gitlab具有严格的权限管理,一般来说这个时候你是没有Maintainer及以上权限的。所以你最好在User组新建 任意等级的项目(这是自己的私人项目)。然后一定要在 Setting Members invite group里邀请dev项目组的成员 这样别人就能看到你的项目,并参与开发了。

1571383767650

接下来就和github新建了项目一样了。

代码规范

为了提高可维护性、可阅读性。我们的项目统一采用了lint规范。所以必须上lint进行开发项目。

git规范: https://yanshuo.io/assets/player/?deck=58f7703ba22b9d006c15edee&tdsourcetag#/

技术文档规范: https://github.com/ruanyf/document-style-guide

前端代码规范: https://ncuhome.github.io/frontend-guide/

后端代码规范:

数据埋点

我们的数据埋点,采用的是Google Analytics的埋点。GoogleAnalytics 所以如果你的项目要面对大流量、之后再进行大数据分析的话,你要联系学长创建 对应你项目的埋点(我们的hanalytics.google 有个组织 Ncuhome,历史的埋点数据都会被集中在里面,以便进行分析)。一般联系你项目对应的产品经理或组长。

控制台参考图

将生成的跟踪代码 放入你的前端页面,就能实现数据收集功能。 Google Analytics 会生成可视化的视图提供你分析。

监控埋点

Sentry是一个实时事件日志记录和汇集的平台。其专注于错误监控以及提取一切事后处理所需信息而不依赖于麻烦的用户反馈。它分为客户端和服务端,客户端(目前客户端有Python,js)就嵌入在你的应用程序中间,程序出现异常就向服务端发送消息,服务端将消息记录到数据库中并提供一个web页方便查看。Sentry由python编写,源码开放,性能卓越,易于扩展 。 http://sentry.ncuos.com 这是我们的sentry,如果的项目需要长时间在线上环境运行,进行线上环境监控是非常有必要的。 创建你项目对应的sentry,然后进行埋点。教程参考下面的链接。

Sentry异常监控方案部署-前端攻略

sentry使用实践

一般重要项目出现服务宕机,会有报警电话(大公司架构)。我们可以配置报警邮件,及时去恢复服务。避免消耗用户的信任。

工作流

我们的git工作流程 采用的是企业级gitflow工作流。

master 分支

master分支上存放的应该是随时可供在生产环境中部署的代码(Production Ready state)。当开发活动告一段落,产生了一份新的可供部署的代码时,master分支上的代码会被更新。

本地分支

在本地创建的开发分支,开发完毕后,将该分支push到我们的gitlab上。 然后线上在 你的分支上提交merge request (提交后记得通知你的学长,让他merge你的commit), 代码经过上级的code review后会merge进入maser分支中。这时你的任务完成。

上线部署

我们的项目目前主要方向都是用CI部署模式,代码被merge入master分支,Travis脚本自动执行,gitlab-ci自动build部署到生产环境。

前端项目建议部署到阿里云的对象存储(OSS)上。访问速度快、无带宽限制,上行流量免费,无需运维人员与托管费用,0成本运维。

后端项目: 后端部署一般需要申请服务器,部署在我们家园自己的服务器上。(其实一般应该直接买一台学生机,练练怎么部署项目)

部署规范

项目部署位置

1
/var/www/project_name

请将所有的项目部署在这个文件夹内,并且要求项目名清晰明了。

Lxxyx学长在2016年写了一份项目及代码部署规范,在这里强调一下部署的规范和一些运维的要求,希望能为后来人的工作提供一点帮助。

项目部署方法

家园即将用上K8S,但在这里还是以Docker部署为主,待到之后更新了K8S在来更新这份文档。

使用Dockerfile和 .dockerignore 定义镜像,使用docker-compose.yml定义容器,在这里指定开放端口,数据卷等,docker-compose管理容器启动,关闭,重启。

日志存放位置

1
/var/log/project_name

使用docker-compose.yml将日志挂载在对应的项目日志文件夹。

项目的日志文件以及报错信息需要按照项目存放,方便查找和处理。

除了容器产生的日志,如果需要使用到nginx的日志功能,将该server产生的日志也存放于此。

Docker容器大小

因为服务器容量有被全部占用完全,导致服务无法正常提供,持续删日志的经历,也为了防止因错误原因持续产生垃圾日志导致服务器别完全占用,请为docker-compose.yml内所有的server加上:

1
2
3
4
logging:
driver: "json-file" // 可自行选择
options:
max-size: "50m" // 可选

服务器申请

我们的服务器申请有自己的一套流程,只需要在 服务器配置 里按照文档添加你的秘钥,你就能获得对应的服务器。申请服务器之前,请先询问你的学长,你能使用哪台服务器。

关于服务器账户

介于此时工作室的体量以及组织情况,目前服务器只需要一个开发用户dev,不需要其他的个人账户

该dev用户拥有sudo权限,所以请在使用该用户时不要进行危险操作

为了方便管理和安全,dev不允许密码登陆,需要使用服务器或部署项目的人需向管理员申请,管理员使用工作室专用的管理项目进行配置。

文档

请注意,每个项目都必须留存有文档,最起码要介绍一下这个项目是做什么用的,部署在什么地方,还有接口文档、这个项目里有什么注意的地方,有什么要给后来的人交代的地方。

项目存档

每个项目必须在语雀上留有存档、该存档一般由项目组成员共同编写。开发如果把文档写好了,只要简单改动下就可以保存在语雀上了。语雀

数据分析

你埋点的东西终于有了回报,看着有那么多人使用你开发的产品,你会有自豪感的!! 可以观察一下某些趋势,写入文档中,尤其是如果某些周期性项目(每年都要做的四六级查询等),一些用户数据存档是非常重要的。

https://analytics.google.com

项目交接

交接方面,一般如果你的项目有了继续迭代下去的需求,就必须考虑传承的问题了。如果你前面的文档写的不错,项目存档做的很好,这个不成问题了,如果做的不好,那请回去补全你的文档。那么以后下一批交接的人如果有问题找你,你只需要在空闲时间抽出时间回复学弟学妹问题就好了。

请对你的用户负责,对你的项目负责,也对你自己负责。

计算机杂谈

计算机组成

计算机组成

计算机硬件系统的基本组成及工作原理
⑴ 计算机硬件由五个基本部分组成:运算器、控制器、存储器、输入设备和输出设备。
⑵ 计算机内部采用二进制来表示程序和数据。
⑶ 采用“存储程序”的方式,将程序和数据放入同一个存储器中(内存储器),计算机能够自动高速地从存储器中取出指令加以执行。
可以说计算机硬件的五大部件中每一个部件都有相对独立的功能,分别完成各自不同的工作。如图1-7所示,五大部件实际上是在控制器的控制下协调统一地工作。首先,把表示计算步骤的程序和计算中需要的原始数据,在控制器输入命令的控制下,通过输入设备送入计算机的存储器存储。其次当计算开始时,在取指令作用下把程序指令逐条送入控制器。控制器对指令进行译码,并根据指令的操作要求向存储器和运算器发出存储、取数命令和运算命令,经过运算器计算并把结果存放在存储器内。在控制器的取数和输出命令作用下,通过输出设备输出计算结果。

计算机的基本组成和工作原理

1496915188338

1497317567484

硬件主板

敲键盘姿势

Windows

查看本机基本配置

怎么查自己电脑的配置

查看隐藏文件、修改文件扩展名。

1570599578880

卸载软件

打开控制面板 程序 卸载程序 找到你要卸载的程序卸载就行了。

强制关闭程序

打开任务管理器 Ctrl+Shift+Esc 找到你要关闭的(进程)程序,点击结束任务。

window简单快捷键

kuaijiejian-3

推荐基本工具

notepad++

NOTEPAD++是一款免费又优秀的文本编辑器,支持在 MS Windows 环境下运行的多种编程语言。NOTEPAD++支持超过 50 种编程、脚本和标记语言的语法高亮显示和代码折叠,能让用户迅速减小或扩大代码段以便查阅整个文档。用户也可以手动设置当前语言,覆盖默认语言。该程序还支持自动完成某些编程语言的 API 子集。

官方网站:http://notepad-plus-plus.org

Typora Markdown文本编辑器 Typora 完全使用详解

代码编辑器 VS Code https://code.visualstudio.com subline text3

IDE WebStorm PyCharm IntelliJ IDEA https://www.jetbrains.com ps: jetbrains家族的东西都是要收费的(有一个月试用期,适用完毕开始收费)。所以需要在学校的官网申请教育邮箱 .edu结尾的邮箱, 然后用这个邮箱到 jetbrains 官网注册学生账号,可以免费使用该产品。

浏览器 Chrome FireFox

Git Git官方网站:https://git-scm.com

浏览器

从浏览器地址栏输入网址,到网页彻底打开,中间都发生了什么?

  • DNS 解析:将域名解析成 IP 地址
  • TCP 连接:TCP 三次握手
  • 发送 HTTP 请求
  • 服务器处理请求并返回 HTTP 报文
  • 浏览器解析渲染页面
  • 断开连接:TCP 四次挥手

URL

什么是URL?

URL指的是统一资源定位符(Uniform Resource Locator。URL无非就是一个给定的独特资源在Web上的地址。

一个URL由不同的部分组成,其中一些是必须的,而另一些是可选的。让我们以下面这个URL为例看看其中最重要的部分:

1
http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument
  • Protocol

    http:// 是协议。它表明了浏览器必须使用何种协议。它通常都是HTTP协议或是HTTP协议的安全版,即HTTPS。Web需要它们二者之一,但浏览器也知道如何处理其他协议,比如mailto:(打开邮件客户端)或者 ``ftp:(处理文件传输),所以当你看到这些协议时,不必惊讶。

  • Domaine Name

    www.example.com 是域名。 它表明正在请求哪个Web服务器。或者,可以直接使用IP address, 但是因为它不太方便,所以它不经常在网络上使用。.

  • Port

    :80 是端口。 它表示用于访问Web服务器上的资源的技术“门”。如果Web服务器使用HTTP协议的标准端口(HTTP为80,HTTPS为443)来授予其资源的访问权限,则通常会被忽略。否则是强制性的。

  • Path to the file

    /path/to/myfile.html 是网络服务器上资源的路径。在Web的早期阶段,像这样的路径表示Web服务器上的物理文件位置。如今,它主要是由没有任何物理现实的Web服务器处理的抽象。

  • Parameters

    ?key1=value1&key2=value2 是提供给网络服务器的额外参数。 这些参数是用 & 符号分隔的键/值对列表。在返回资源之前,Web服务器可以使用这些参数来执行额外的操作。每个Web服务器都有自己关于参数的规则,唯一可靠的方式来知道特定Web服务器是否处理参数是通过询问Web服务器所有者。

  • Anchor

    #SomewhereInTheDocument 是资源本身的另一部分的锚点. 锚点表示资源中的一种“书签”,给浏览器显示位于该“加书签”位置的内容的方向。例如,在HTML文档上,浏览器将滚动到定义锚点的位置;在视频或音频文档上,浏览器将尝试转到锚代表的时间。值得注意的是,#后面的部分(也称为片段标识符)从来没有发送到请求的服务器。

什么是URL?

浏览器解析渲染页面

1456355-8883dbd15f39cc18

解析后生成的DOM树

1497169919418

常见的浏览器软件包括:

  • Google的Chrome。Webkit内核(新版转向Blink)和V8 JS引擎
  • 微软的IE。Trident内核,IE 11之后不再更新。
  • 微软的Edge。EdgeHTML内核,Chakra JS引擎。
  • Mozilla的Firefox。Gecko内核和 SpiderMonkey JS引擎。
  • 苹果的Safari。Webkit内核,Nitro JS引擎。
  • Opera。原Presto,新版也转向Blink和V8。

2018年11月,全球&国内浏览器市场份额排行榜

搜索引擎使用(搜索技巧)

1、双引号

把搜索词放在双引号中,代表完全匹配搜索,也就是说搜索结果返回的页面包含双引号中出现的所有的词,连顺序也必须完全匹配。bd和Google 都支持这个指令。例如搜索: “seo方法图片”

2、减号

减号代表搜索不包含减号后面的词的页面。使用这个指令时减号前面必须是空格,减号后面没有空格,紧跟着需要排除的词。Google 和bd都支持这个指令。
例如:搜索 -引擎
返回的则是包含“搜索”这个词,却不包含“引擎”这个词的结果

3、星号
星号是常用的通配符,也可以用在搜索中。百度不支持号搜索指令。
比如在Google 中搜索:搜索
其中的
号代表任何文字。返回的结果就不仅包含“搜索引擎”,还包含了“搜索收擎”,“搜索巨擎”等内容。

4、site
site:是SEO 最熟悉的高级搜索指令,用来搜索某个域名下的所有文件。

site:www.yuque.com

5、related
related:指令只适用于Google,返回的结果是与某个网站有关联的页面。比如搜索

related:www.ncu.edu.cn

6、filetype
用于搜索特定文件格式。Google 和bd都支持filetype 指令。
比如搜索

SEO filetype:pdf
返回的就是包含SEO 这个关键词的所有pdf 文件。

如何用好谷歌等搜索引擎?

Chrome浏览器使用

浏览器快捷键

1570719077546

开发者工具

打开方式有3种: 第一“按F12”,第二:shift+ctrl+i,第三: 鼠标右键 检查

Console

11994491-8518ebbd3428c691

Elements

11994491-f9287dcb83bb4647

11994491-1e36768a70f6a8cc

Network

1570714164775

Elements:查找网页源代码HTML中的任一元素,手动修改任一元素的属性和样式且能实时在浏览器里面得到反馈。

Console:记录开发者开发过程中的日志信息,且可以作为与JS进行交互的命令行Shell。

Sources:断点调试JS。

Network:从发起网页页面请求Request后分析HTTP请求后得到的各个请求资源信息(包括状态、资源类型、大小、所用时间等),可以根据这个进行网络性能优化。

Application:记录网站加载的所有资源信息,包括存储数据(Local Storage、Session Storage、IndexedDB、Web SQL、Cookies)、缓存数据、字体、图片、脚本、样式表等。

Security:判断当前网页是否安全。

Audits:对当前网页进行网络利用情况、网页性能方面的诊断,并给出一些优化建议。比如列出所有没有用到的CSS文件等。

Chrome DevTools(https://developers.google.com/web/tools/chrome-devtools)

常用网站

GitHub https://github.com

作为开源代码库以及版本控制系统,Github拥有140多万开发者用户。随着越来越多的应用程序转移到了云上,Github已经成为了管理软件开发以及发现已有代码的首选方法。代码托管必备。

MDN https://developer.mozilla.org web权威文档

MDN Web Docs 网站提供开放网络(Open Web)技术有关的信息,包括用于网站和渐进式网络应用的 HTML、CSS 和 API。

技术学习网站

慕课网(IMOOC)是IT技能学习平台。慕课网(IMOOC)提供了丰富的移动端开发、php开发、web前端、android开发以及html5等视频教程资源公开课。并且富有交互性及趣味性,你还可以和朋友一起编程。 https://www.imooc.com

菜鸟教程 http://www.runoob.com 入门学习网站

w3school https://www.w3school.com.cn 入门学习网站

技术问答

StackOverFlow技术问答 http://stackoverflow.com

是一个与程序相关的IT技术问答网站。用户可以在网站免费提交问题,浏览问题,索引相关内容,在创建主页的时候使用简单的HTML

技术博客

掘金 https://juejin.im 思否 https://segmentfault.com 简书 https://www.jianshu.com csdn(…)

知乎 https://zhihu.com 其实知乎上也有很多不错的技术博客

Chrome插件下载 [扩展迷] https://extfans.com/

Chrome DevTools https://developers.google.com/web/tools/chrome-devtools/

github仓库

在这里再推荐几个github项目:

本文介绍的是利用学生身份可以享受到的相关学生优惠权益

web学习路线

免费的计算机编程类中文书籍

一、初始化项目文件夹

在任意目录下,新建一个文件夹作为你的项目文件夹,命名随意。随后使用命令行工具,切换到该文件夹,键入npm init进行初始化(遇到的问题一直回车就好了),初始化完成之后可以看到生成了一个package.json文件。

随后在该项目文件夹下新建两个文件夹:/dist/src,其中/src用于放置开发的源码,/dist用于放置“编译”后的代码。

随后在/src目录下新建index.htmlindex.cssindex.js文件

按照以下内容创建文件:

index.html

1
2
3
4
5
6
7
8
9
10
11
<html>
<head>
<meta charset="utf-8"/>
<title>React & Webpack</title>
</head>
<body>
<div id="root">
<h1>Hello React & Webpack!</h1>
</div>
</body>
</html>

index.css

1
2
3
4
5
6
7
8
body {
background-color: blue;
}

#root {
color: white;
background-color: black;
}

index.js

1
2
3
import './index.css';

console.log('Success!');

二、安装webpack工具

通过命令行使用webpack 4需要安装两个模块:webpack和webpack-cli,都安装为开发环境依赖。

1
npm install -D webpack webpack-cli

安装完成之后可以看到你的package.json文件发生了变化,在devDependencies属性下多了两个包的属性。

三、配置最基本的webpack

  • 1.安装最基本的插件:

    1
    npm install -D html-webpack-plugin clean-webpack-plugin webpack-dev-server css-loader webpack-merge style-loader
  • 2.在项目文件夹下新建文件webpack.base.conf.js,表示最基本的配置文件,内容如下:

````webpack.base.conf.js`
const path = require(‘path’);
const HtmlWebpackPlugin = require(‘html-webpack-plugin’);
const { CleanWebpackPlugin } = require(‘clean-webpack-plugin’)

module.exports = {
entry: ‘./src/index.js’,
output: {
filename: ‘bundle.[hash].js’,
path: path.join(__dirname, ‘/dist’) //打包生成文件地址
},
module: {
rules: [
{
test: /.css$/,
use: [‘style-loader’, ‘css-loader’]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: ‘./src/index.html’
}),
new CleanWebpackPlugin({
cleanAfterEveryBuildPatterns: [‘./dist’]
})
]
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

其中,`/src/index.html`是你的网站入口HTML文件,`/src/index.js`是你的入口js文件。

.在项目文件夹下新建`webpack.dev.conf.js`文件,表示开发环境下的配置。内容如下:

````webpack.dev.conf.js`
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.conf.js');

module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
port: 3000
}
});

在项目文件夹下新建webpack.prod.conf.js文件,表示生产环境的配置,内容如下:

1
2
3
4
5
6
7
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.conf.js');

console.log(__dirname);
module.exports = merge(baseConfig, {
mode: 'production'
});

四、配置npm scripts

配置了三个配置文件以满足两个不同环境下的代码构建,使用语义化较好的npm scripts来构建代码有利于简化工作。

添加新的scripts内容到package.json文件的scripts属性,记得用双引号引起来,其属性如下:

1
2
3
4
5
6
7
// package.json
{
"scripts": {
"start": "webpack-dev-server --open --config webpack.dev.conf.js",
"build": "webpack --config webpack.prod.conf.js"
}
}

配置完之后,可以尝试修改/src/index.html/src/index.js/src/index.css,运行npm run start命令查看效果。

做到这里,一个基本的开发环境已经搭建出来了。

五、配置Babel

Babel是一个优秀的JavaScript编译器(这句话源自Babel官网),通过Babel的一些插件,可以将JSX语法、ES6语法转换为ES5的语法,使得低级浏览器也可以运行我们写的代码。

安装 babel-loader@babel/core@babel/preset-env@babel/preset-react 作为 dev 依赖项

1
npm i babel-loader@8 @babel/core @babel/preset-env @babel/preset-react -D
  • babel-loader:使用 Babel 转换 JavaScript依赖关系的 Webpack 加载器
  • @babel/core:即 babel-core,将 ES6 代码转换为 ES5
  • @babel/preset-env:即 babel-preset-env,根据您要支持的浏览器,决定使用哪些 transformations / plugins 和 polyfills,例如为旧浏览器提供现代浏览器的新特性
  • @babel/preset-react:即 babel-preset-react,针对所有 React 插件的 Babel 预设,例如将 JSX 转换为函数

**注:babel 7 使用了 @babel 命名空间来区分官方包,因此以前的官方包 babel-xxx 改成了 @babel/xxx

修改webpack.base.conf.js 和 创建.babelrc 文件,并配置 babel-loader 及 babel 选项

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
}
};
1
2
3
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

六、配置react

1
npm install --save react react-dom

/src中新建一个App.js文件,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';

class App extends React.Component {
render() {
return <div>
<h1>Hello React & Webpack!</h1>
<ul>
{
['a', 'b', 'c'].map(name => <li>{`I'm ${name}!`}</li> )
}
</ul>
</div>
}
}

export default App;

清空index.js之后在其中写入如下内容:

1
2
3
4
5
6
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';

ReactDOM.render(<App/>, document.getElementById('root'));

使用npm run start命令打开页面可以看到使用React写出来的效果了。

打开浏览器查看编译后的代码,找到App组件中的map函数这一段,可以发现ES6的语法已经被转换到了ES5的语法:

1
2
3
4
5
6
7
['a', 'b', 'c'].map(function (name) {
return _react2.default.createElement(
'li',
null,
'I\'m ' + name + '!'
);
})

箭头函数被写成了function匿名函数。

七、配置TypeScript

执行以下命令安装 TypeScript compiler 和 loader:

1
npm install --save-dev typescript ts-loader

在根目录新建一个tsconfig.json文件

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "commonjs",
"target": "es6",
"jsx": "react"
},
"include": [
"./src/**/*"
]
}

然后在webpack.dev.conf.js配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const path = require('path');

module.exports = {
entry: './src/index.js',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};

这个时候就可以使用ts了。 ps: 在ts中引用都要申明类型。

1
import * as React from "react"; // 这样没有申明的react会报错、 所以应该npm install @types/react

参考

基于Webpack搭建React开发环境

使用 Webpack 4 和 Babel 7 从头开始创建 React 应用程序

TypeScript

前言

使用阿里云OSS + CDN方案的几大好处:

  • 成本低廉。OSS+CDN部署自己的网站每个月的花费远比自己买ECS服务器部署网站花费要少得多
  • 大幅降低运维成本。直接使用现成的云服务了,无需花精力去维护ECS了。
  • 极高的可用性。无论阿里云的OSS还是CDN,都已经做好了高可用性,几乎可以保证网站始终可访问
  • 极高的访问速度。ECS带宽毕竟有限的,高带宽意味着超高的费用。但是用OSS+CDN这种天然分布式的架构部署网站很轻松的解决了带宽问题,极大地提升了用户的访问体验。
  • OSS会自动帮你压缩,使得你的页面加载速度极快。

为此我写了一篇博客来帮助指导使用OSS,并且写了一个部署脚本deploy.js来实现自动上传。js_deploy

配置 阿里云对象存储(OSS)

首先登陆阿里云oss控制台,点击创建一个bucket。

1569852346504

根据自己的需求选择参数。

配置bucket

然后就创建了一个bucket。

1569852577526

为bucket配置域名,在上图中,将阿里云外网访问Bucket域名记录下来。然后在DNS控制台添加记录解析。然后回到oss控制台,在域名管理选项 将你刚刚DNS解析的域名 绑定上去。这样就可以通过自己设置的二级域名访问到自己的项目了。

Bucket域名

DNS控制台解析

域名管理

基础设置找到静态页面 设置默认首页的文件名 一般都是index.html,如果有404页面也可以配置。

1569853929467

就下来就只要将自己的打包出来的静态文件 通过deploy.js脚本上传到OSS上就行了

配置deploy.js

打开deploy.js将bucket,region填入。key,secret可能忘记(在刚开始使用阿里云的时候,阿里云会将这个发送给你,并提醒你保存),可以在用户信息管理 安全信息管理获取到自己的Access Key Secret(也可以在这里创建一个新的AccessKey)。 因为deploy.js中保存有你的阿里云accessKeyId与accessKeySecret 所以记得在.gitignore文件忽略deploy.js

1569854391813

用户信息管理界面

这样再稍微配置下要部署的项目 就可以用这个脚本了。

使用

将deploy.js下载到你的项目根目录下。一般是webpack打包而成的单页面应用。页面打包生成dist文件夹目录,将dist文件夹上传到阿里云oss上。

在package.json中加入这个脚本命令,用来更快的执行部署命令。也可以手动node deploy.js执行部署脚本。

1
2
3
4
5
6
"scripts": {
"deploy": "node deploy.js"
},
"devDependencies": {
"ali-oss": "^6.1.1", // 这是阿里云的oss依赖,也可以直接手动npm install ali-oss --save-dev
}

演示

在这里 我演示一个将ant-design-pro构建的项目打包上传到oss上。

上传成功

然后就可以访问到了(记得一定要在oss控制台设置index.html为首页)

访问成功

在这里你可以通过访问 http://example-oss.flura.cn 访问到我的阿里云示例

webpack项目自动部署到阿里云OSS

这里做一个补充:如果是一个webpack项目 我们使用一个webpack插件,可以实现自动部署到阿里oss的效果。可以极大的提升开发效率,开发完毕,打包项目就可以部署到线上环境。

aliyunoss-webpack-plugin插件使用

  1. 首先安装aliyunoss-webpack-plugin插件
1
2
yarn add aliyunoss-webpack-plugin -D
// 或者使用 npm i aliyunoss-webpack-plugin -D
  1. 修改webpack.prod.conf.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const AliyunossWebpackPlugin = require('aliyunoss-webpack-plugin')
const oss = require('../oss')
...
plugins: [
...
// 插入到最后面
new AliyunossWebpackPlugin({
buildPath: 'dist/**',
region: oss.region,
accessKeyId: oss.accessKeyId,
accessKeySecret: oss.accessKeySecret,
bucket: oss.bucket,
deleteAll: true,
generateObjectPath: function(filename, file) {
return file.replace(/dist\//, '')
},
})
}

创建一个oss.js 因为不希望 阿里云oss隐私信息被上传到git所以 记得在.gitignore文件中忽略。

现在只要执行npm run build就能打包并上传到oss上实现自动部署了。

deploy.js github仓库链接

github仓库

部署示例

本文是我配置一个Gitlab CI实现一个前端项目自动打包部署的踩坑体会。

背景

为什么要去配置这么一个自动化部署CI,这个需求是什么?

我所接手的这一个项目是比较老比较大的项目(vue),它依赖了一些很麻烦的包,这些包很难在window下环境友好运行,所以导致这个项目无法打包部署。这给我们整个团队带来了很大的不便,部署只能用linux很麻烦的手动部署。(学生党不可能人人有钱买Mac吧),所以我想配置一个gitlab-ci实现项目的自动部署,以提升效率。

介绍

Gitlab

一个基于git实现的在线代码仓库软件,你可以用Gitlab自己搭建一个类似于Github一样的系统,一般用于在企业、学校等内部网络搭建Git私服。

什么是持续集成/持续部署(CI/CD)?

阮一峰有篇文章很好的介绍了持续集成是什么?

持续集成指的是,频繁地(一天多次)将代码集成到主干。

持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。

通俗易懂的来说就是把 代码测试、打包、发布等工作交给一些工具来自动完成、这样可以提高很多效率,减少失误,开发人员只要关心把代码提交到git就行了。

Gitlab的CI

从 GitLab 8.0 开始,GitLab CI 就已经集成在 GitLab 中,我们只要在项目中添加一个 .gitlab-ci.yml 文件,然后添加一个 Runner,即可进行持续集成。 而且随着 GitLab 的升级,GitLab CI 变得越来越强大。

.gitlab-ci.yml

在git项目的根目录下的一个文件,记录了一系列的阶段和执行规则。GitLab-CI在push后会解析它,根据里面的内容调用runner来运行。

简单来说就是,你利用Git版本管理Push了本地代码到你的gitlab.com上,然后Gitlab,就通知你的服务器(runner服务器),也就是Gitlab-runner来运行构建任务。然后跑测试用例,测试用例通过了就生成Build出相应的环境的代码,自动部署上不同的环境服务器上面去。

GitLab-Runner

这个是脚本执行的承载者,.gitlab-ci.yml的script部分的运行就是由runner来负责的。GitLab-CI浏览过项目里的.gitlab-ci.yml文件之后,根据里面的规则,分配到各个Runner来运行相应的脚本script。这些脚本有的是测试项目用的,有的是部署用的。

1569760156912

快速开始

简而言之,CI所需要的步骤可以归结为:

  1. 添加.gitlab-ci.yml到项目的根目录

  2. 配置一个Runner

从此刻开始,在每一次push到Git仓库的过程中,Runner会自动开启pipline,pipline将显示在项目的Pipline页面中。


本指南要求:

  • 使用版本8.0+ 的GitLab实例或者是使用GitLab.com
  • 一个想使用GitLab CI的项目

配置.gitlab-ci.yml

  1. 在项目的根目录创建一个名为.gitlab-ci.yml的文件。注意:.gitlab-ci.yml是一个*&####&10&####&*文件,所以必须要格外注意锁紧。使用空格,而不是tabs。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    image: node  # 选用docker镜像

    stages: # Stages 表示构建阶段,这里有两个阶段 install, deploy
    - install
    - deploy

    install-staging:dep: # Jobs 表示构建工作,表示某个 Stage 里面执行的工作。
    stage: install
    only: # 定义了只有在被merge到了master分支上 才会执行部署脚本。
    - master
    script:
    - echo "=====start install======"
    - npm install --registry=https://registry.npm.taobao.org #安装依赖
    - echo "=====end install======"
    artifacts: # 将这个job生成的依赖传递给下一个job。需要设置dependencies
    expire_in: 60 mins # artifacets 的过期时间,因为这些数据都是直接保存在 Gitlab 机器上的,过于久远的资源就可以删除掉了
    paths: # 需要被传递给下一个job的目录。
    - node_modules/

    deploy-staging:dep:
    stage: deploy
    only:
    - master
    script:
    - echo "=====start build======"
    - npm run build # 将项目打包
    - echo "=====end build======"
    - echo "=====start deploy======"
    - npm run deploy # 此处执行部署脚本,将打包好的静态文件上传到阿里云的oss上,为了保护项目安全,使抽象成deploy步骤。
    - echo "=====end deploy!!!!!!======"

这是我的**.gitlab-ci.yml脚本**。(# 为.gitlab-ci.yml脚本注释)

image

1
image: node  # 选用docker镜像
我项目的 CI 任务是选的在 Docker 上运行,所以每次执行 CI 任务的时候,都会新启动一个 Docker 容器。因     为是前端项目,所以需要node环境。所以选用的是node镜像。也可以选择自己的docker镜像。

stages

1
2
3
stages: # Stages 表示构建阶段,这里有两个阶段 install, deploy
- install
- deploy

Stages 表示构建阶段,说白了就是上面提到的流程。 我们可以在一次 Pipeline 中定义多个 Stages,每个Stage可以完成不同的任务。 Stages有下面的特点:

  • 所有 Stages 会按照顺序运行,即当一个 Stage 完成后,下一个 Stage 才会开始

  • 只有当所有 Stages 完成后,该构建任务 (Pipeline) 才会成功

  • 如果任何一个 Stage 失败,那么后面的 Stages 不会执行,该构建任务 (Pipeline) 失败

only

1
2
only:
- master

只有maser分支才会触发这个脚本,因为我们采用的git-flow工作流,开发人员可能把自己未完善的分支(没有经过上级code review)提交到线上仓库,那么只要有push就会触发部署到线上环境,这样的后果是不堪设想的,所以必须加一个only,只有经过了code review的代码 被merge进入了maser分支才会实现部署到线上环境。

Jobs

1
2
install-staging:dep: # Jobs 表示构建工作,表示某个 Stage 里面执行的工作。
stage: install

Jobs 表示构建工作,表示某个 Stage 里面执行的工作。 我们可以在 Stages 里面定义多个 Jobs,这些 Jobs 会有以下特点:

  • 相同 Stage 中的 Jobs 会并行执行

  • 相同 Stage 中的 Jobs 都执行成功时,该 Stage 才会成功

  • 如果任何一个 Job 失败,那么该 Stage 失败,即该构建任务 (Pipeline) 失败

script

1
2
3
4
script:
- echo "=====start install======"
- npm install --registry=https://registry.npm.taobao.org
- echo "=====end install======"

script是一段由Runner执行的shell脚本

artifact

1
2
3
4
artifacts:  # 将这个job生成的依赖传递给下一个job。需要设置dependencies
expire_in: 60 mins # artifacets 的过期时间,因为这些数据都是直接保存在 Gitlab 机器上的,过于久远的资源就可以删除掉了
paths:
- node_modules/

artifacts 被用于在job作业成功后将制定列表里的文件或文件夹附加到job上,传递给下一个job,如果要在两个job之间传递artifacts,你必须设置dependencies

脚本总结

总结: 这个脚本的作用是 将merge进入master分支的代码打包并部署到阿里云的oss上。这里最值得注意的就是artifact,因为定义了两个job,其实每个job都是用的新的镜像,所以这样就会导致install阶段与deploy阶段没有任何关系,但是实际上deploy阶段是依赖install阶段安装的node_module的。所以必须将install阶段安装的 node_modules传递给下一个job(deploy),这就需要用到artifact或者cache了(这里我用的是artifact)。ps:我还其实还把这两个job整合成一个了,但是不知道为什么明明两个分开执行就只要10min,而合在一个job就要超过1h,最后导致超时Pipeline失败。

更多详细配置可以看这篇博客gitlab-ci配置详解

或者参考官方文档 官方配置文档

  1. 推送.gitlab-ci.yml到GitLab

    一旦创建了.gitlab-ci.yml,你应该及时添加到Git仓库并推送到GitLab。

    现在到Pipelines页面查看,将会看到该Pipline处于等待状态。

    1569815810780

配置Runner

在GitLab中,Runners将会运行你在.gitlab-ci.yml中定义的jobs。Runner可以是虚拟机,VPS,裸机,docker容器,甚至一堆容器。GitLab和Runners通过API通信,所以唯一的要求就是运行Runners的机器可以联网。

一个Runner可以服务GitLab中的某个特定的项目或者是多个项目。如果它服务所有的项目,则被称为共享的Runner。

Runners文档中查阅更多关于不同Runners的信息。

你可以通过Settings->CI/CD查找是否有Runners分配到你的项目中。创建一个Runner是简单且直接的。官方支持的Runner是用GO语言写的,它的文档在这里https://docs.gitlab.com/runner/。

为了有一个功能性的Runner,你需要遵循以下步骤:

安装

  1. 添加Gitlab的官方源:

    1
    2
    3
    4
    5
    # For Debian/Ubuntu
    curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | sudo bash

    # For CentOS
    curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.rpm.sh | sudo bash
  2. 安装

    1
    2
    3
    4
    5
    # For Debian/Ubuntu
    sudo apt-get install gitlab-ci-multi-runner

    # For CentOS
    sudo yum install gitlab-ci-multi-runner
  3. 注册Runner
    Runner需要注册到Gitlab才可以被项目所使用,一个gitlab-ci-multi-runner服务可以注册多个Runner。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $ sudo gitlab-ci-multi-runner register

    Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
    https://mygitlab.com/ci
    Please enter the gitlab-ci token for this runner
    xxx-xxx-xxx
    Please enter the gitlab-ci description for this runner
    my-runner
    INFO[0034] fcf5c619 Registering runner... succeeded
    Please enter the executor: shell, docker, docker-ssh, ssh?
    Please enter the Docker image (eg. ruby:2.1):
    node
    INFO[0037] Runner registered successfully. Feel free to start it, but if it's
    running already the config should be automatically reloaded!

ps: 这里面的gitlab-ci coordinator URL 与token

1
2
3
4
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
https://mygitlab.com/ci
Please enter the gitlab-ci token for this runner
xxx-xxx-xxx

是在gitlab配置对应的runner里可以查看的。

1569816937998

1569817315527

配置

按照上面的连接设置你自己的Runner,我这边因为这是专用对于一个项目的runner,所以我配置的是specific runner。

一旦Runner安装好,你可以从项目的Settings->CI/CD找到Runner页面。

1569816937998

1569817028804

参考