vue-admin 详细注释,必须手把手做项目系列之(一)

栏目: JavaScript · 发布时间: 6年前

内容简介:这是公司的一个项目的阉割版,可以用作模板,里面的技术点基本全部都有注释,全都有注释,全都有注释,重要事说三遍, 写注释很辛苦,给个小赞,github给个小start,鼓励鼓励码字的我,项目正在陆续的完善中。还有重申一点,我们是前端工程师,不是码农。麻雀虽小五脏俱全:[项目地址

这是公司的一个项目的阉割版,可以用作模板,里面的技术点基本全部都有注释,全都有注释,全都有注释,重要事说三遍, 写注释很辛苦,给个小赞,github给个小start,鼓励鼓励码字的我,项目正在陆续的完善中。还有重申一点,我们是前端工程师,不是码农。

麻雀虽小五脏俱全:[项目地址 github.com/whylisa/vue… )

项目功能

  • 1 登录
  • 2 首页
  • 3 退出
  • 4 table页

项目技术点

  • 1,使用vue
  • 2,使用echarts
  • 3, 使用json-server (系列二写详细文档)
  • 4, 使用node起一个简单的服务,服务于接口(系列二写详细文档)
  • 5, 使用axios
  • 6, vue-router的使用规则(异步加载,和同步加载)
  • 7, 回话拦截的使用(localstorage or cookie)
  • 8, 配合element-UI
  • 9, 修改组件里面的样式里面的坑
  • 10, 打包时的优化
  • 11, DNS优化
  • 12, 配置本地代理,使用接口(系列二写详细文档)
  • 13, 使用axios配合json-server 模拟增删改查(系列二写详细文档)
  • 14, 使用nprogress 插件
  • 15, 鲜为人知的element-UI 的滚动条
  • 16, 栅格布局,大小屏适应配合媒体查询
  • 17, css使用less
  • 18, 代码风格,个人风格,禁用了jslint 防止不懂得小伙伴抓狂
  • 19, 兼容性的处理(系列二写详细文档)

项目搭建

  • 1 vue init webpack XX 使用vue-cli 2.0
Project name                            :默认
Project description                     :默认
Author                                  :默认
Vue build                               :选择 Runtime + Compiler
Install vue-router?                     :Y
Use ESLint to lint your code?           :Y 选择 Standard
Set up unit tests                       :n
Setup e2e tests with Nightwatch?        : n
Should we run `npm install` for you after the project has been created? (recommended) : Yes, use NPM
复制代码
  • 2 进入项目:cd vue-admin-stepbystep
  • 3 运行项目:npm run dev

如何添加一个新的功能???

components
router/index.js

在项目中使用 element-ui(其他自行gg加深映像)

// main.js

// 导入elementui - js
import ElementUI from 'element-ui'
// 导入elementui - css
import 'element-ui/lib/theme-chalk/index.css'
// 安装插件
Vue.use(ElementUI)
复制代码

项目启动做了什么

  • 1 在终端中运行: npm run dev ,实际上就是运行了: webpack-dev-server ...
  • 2 使用 webpack-dev-server 开启一个服务器
  • 3 根据指定的入口 src/main.js 开始分析入口中使用到的模块
  • 4 当遇到 import 的时候,webpack 就会加载这些模块内容(如果有重复模块,比如:Vue,实际上将来只会加载一次),遇到代码就执行这些代码
  • 5 创建 Vue 实例,将 App 组件作为模板进行编译,并且将 App 组件中 template 的内容渲染在页面 #app 的位置

路由配置

  • 1 异步加载路由
  • 2 使用进度条插件
  • 3 登录拦截 会话保持
import Vue from 'vue'
import Router from 'vue-router'
//引入nprogress进度条
import NProgress from 'nprogress'
//引入nprogress进度条的样式
import 'nprogress/nprogress.css'
//在打包过程中每一个组件都会打包成一个js文件,如果不使用使用/* webpackChunkName: "home" */
//在打包的时候就会生成0.js,1.js等等,使用了之后就会打包成home.js
// 导入 Login 组件(注意,不要添加 .vue 后缀)
//这是路由的异步加载,!important,这是优化项目必须的

//引入home组件
const Home = () => import(/* webpackChunkName: "home" */ '@/components/home')
//引入登录组件
const Login = () => import(/* webpackChunkName: "home" */ '@/components/login')
//引入table组件
const Table = () => import(/* webpackChunkName: "home" */ '@/components/table/table')
//引入homeMain组件
const HomeMain = () => import('@/components/HomeMain')

//这里是同步加载
//import Login from '@/components/login/Login'

Vue.use(Router)

const router = new Router({
	mode: 'history',//开启了history模式,去除了#,
	 // 在vue中,一般来说通过实例去访问某个属性的
     // vm.xxxx  vm.$set  vm.$refs  vm.$router
	routes: [
		{
			path: '/',
			redirect: '/homeMain'//路由的重定向
		},
		{
			path: '/login',
			name: 'login',
			component: Login
		},
		{
			path: '/home',
			name: 'home',
			component: Home,
			// children 用来配置子路由,将来匹配的组件会展示在 Home 组件的 router-view 中
		    // 对于子路由path来说:
		    // 1 如果不是以 / 开头,那么,哈希值为: 父级path + / + 子级path
		    //    也就是: /home/homeMain
		    // 2 如果子级路由的path是以 / 开头的,那么将来的哈希值为:/users 不再带有父级的path了
            //    也就是:/homeMain
            //这是页面中的子路由,在页面中必须声明router-view作为出口
			children: [
				{
					path: '/homeMain',
					name: 'homeMain',
					component: HomeMain
				},
				{
					path: '/table',
					name: 'table',
					component:Table
				}
			]
		}
	]
});
// 给router配置导航守卫
// to: 去哪儿
// from: from 哪儿来
// next() :  next():放行   next('/login') 去login组件
// 在登录成功以后,将 token 存储到 localStorage 中
// 在 导航守卫 中先判断当前访问的页面是不是登录页面
// 如果是登录页面,直接放行(next())
// 如果不是登录页面,就从 localStorage 中获取 token,判断有没有登录
// 如果登录了,直接放行(next())
// 如果没有登录,就跳转到登录页面让用户登录(next('/login')
router.beforeEach((to, from, next) => {
//	开启进度条
	NProgress.start()
//	获取是否有token
	let token = localStorage.getItem('myToken')
	// 如果已经就是要去login了,就不需要拦截了
	if (to.path === '/login' || token) {
		next()
	}else {
		next('/login')
	}
	
});
router.afterEach(() => {
//	关闭进度条
	NProgress.done()
})

export default router
复制代码

登录功能

npm i -S axios
Login.vue
<div class="l-right">
   		<div class="l-l">
   			 <!-- @tab-click="handleClick" -->
   			<el-tabs v-model="activeName">
   				<el-tab-pane label="用户登录" name="first">
   					<!-- el-form:自定义表单组件 -->
   				    <!-- :model="form" 表单对象,用于收集收据 -->
   				    <!-- label-width="80px":label的宽度 -->
   				    <!-- el-form-item:表单项  -->
   					<el-form ref="form" status-icon :rules="rules" :model="form" label-width="80px">
   						<el-form-item  prop="username">
   							<el-input v-model="form.username" placeholder="请输入用户名" prefix-icon="iconfont icon-yonghuming"></el-input>
   						</el-form-item>
   						<el-form-item  prop="password">
   							<!-- 将来我们给组件注册事件的时候,可以会注册不上 -->
   							<!--@keyup.enter点击键盘的enter触发事件-->
   							<!-- .native: 注册事件,给组件的根元素注册事件 -->

   							<el-input type="password" v-model="form.password" placeholder="请输入密码" @keyup.enter.native="login" prefix-icon="iconfont icon-mima"></el-input>
   						</el-form-item>
   						<el-form-item>
   							<!--使用@语法糖绑定事件-->
   							<el-button type="primary" @click="login">登录</el-button>
   							<el-button @click="reset">重置</el-button>
   						</el-form-item>
   					</el-form>
   				</el-tab-pane>
   				<el-tab-pane label="帅哥登录" name="second">长得很帅</el-tab-pane>
   			</el-tabs>
   		</div>
   		
   	</div>
   </div>
复制代码
export default {
		data () {
			return {
//				定义一些变量,可以使用{{}}语法在页面中直接获取
				activeName: 'first',
				form: {
					username: 'why',
					password: "123456"
				},
				rules: {
					// 用户名的校验
					username: [
						// 用户名是必须
//						required是否必须
//						message提示信息
//						trigger如何触发
						{ required: true, message: '请输入用户名', trigger: 'change' },
						{ min: 3, max: 6, message: '长度在 3 到 6 个字符', trigger: 'change' }
					],
					// 密码的校验
					password: [
						{ required: true, message: '请输入密码', trigger: 'change' },
						{ min: 6, max: 12, message: '长度在 6 到 12 个字符', trigger: 'change' }
					]
				}
			}
		},
		methods: {
			login () {
//			 先触发页面中的检验规则,不通过给提示,通过就向后台发送请求,
//           $refs是vue中获取页面的,在html中要写 ref="form"
		     this.$refs.form.validate(async (valid) => {
				 if (valid) {
//				 	使用axios向后台发送请求
//                  在es6中的箭头函数没有this绑定,可以打印出来指向的是vue实例,这点可以自行百度,加深映像
					 this.axios('/api/login').then( res => {
						 console.log(res.data[0])//用来查看接口里面的数据
						 let lg = res.data[0] //把数据赋值给变量
						 console.log(lg.username,lg.password)//主要用来查看数据
						 if(lg.username === this.form.username && lg.password==this.form.password){
							 localStorage.setItem('myToken',lg.username)//设置拦截,可以用cookie等,在控制台中的Application中查看
							 this.$message.success('恭喜你,登录成功')//登录成功的提示
							 this.$router.push('homeMain') //使用编程式导航路由进行跳转
						 }else {
							 this.$message.error('账号或者密码错误')//账号密码错误时的提示

						 }
					 })
				 }
			 })
			},
			reset () {
				this.$refs.form.resetFields()//清空输入框中的信息
//				数据被我写死了,可以自行改动
			}
		}
  };
复制代码

顶部和侧边

<el-container>
			<!--给el-header设置 高度-->
			<el-header style="height: 70px;">
				<div class="logo">
					<!--这里可以放一般网站的logo-->
					<!--<img src="../assets/main/logo.png" alt="">-->
				</div>
				<div class="header-right">
					<div class="logout"  @click="layout">
					    <!--javascript:;为了防止a标签的默认行为,-->
						<a href="javascript:;">退出</a>
					</div>
					<div class="people">
						<!--映入iconfont 的字体图标-->
						<i class="iconfont icon-lianxirenwode"></i>
						张三
					</div>
					<div class="call">
						<i class="iconfont icon-lianxiwomen"></i>
						联系我们
					</div>
				</div>
			</el-header>
			<el-container>
				<el-aside width="160px" background-color="#26292E">
					<el-scrollbar style="height: 100%;">
						  <!-- el-menu: 导航菜单的组件 -->
				          <!-- default-active:默认高亮的菜单 -->
				          <!-- open close 展开和关闭的事件 -->
				          <!-- el-submenu: 子菜单 -->
				          <!-- el-menu-item-group: 子菜单中分组 -->
				          <!-- el-menu-item:子菜单中的每一项 -->
				          <!-- unique-opened: 保证只能打开一个子菜单 -->
				          <!-- router: 如果router为true,那么index就会作为路由的连接 -->
				          
						<el-menu :unique-opened='true' :router="true" text-color="#ffffff" active-text-color="#cccccc">
							<el-submenu index="1">
								<template slot="title"><i class="iconfont icon-shouye"></i>
									<span @click="gomain">
										首页
									</span>
								</template>
							</el-submenu>
						
							<el-submenu index="2">
								<template slot="title"><i class="iconfont icon-message-channel"></i>table</template>
								<el-menu-item-group>
									<el-menu-item index="/table">table</el-menu-item>
								</el-menu-item-group>
							</el-submenu>
						</el-menu>
					</el-scrollbar>
				</el-aside>
				<el-container>
					<!--使用element的自带的滚动条,官方文档没有-->
					<el-scrollbar style="height: 100%;width: 100%;">
					<el-main>
						 <keep-alive>
				            <!-- 这里是会被缓存的视图组件 -->
				            <!-- $route.meta.keepAlive:如果是true,
				                 说明是缓存组件,通过keep-alive这个标签把缓存组件显示出来 -->
			              <router-view v-if="$route.meta.keepAlive">
			              </router-view>
			             </keep-alive>
			          <!-- 这里是不被缓存的视图组件 -->
			          <router-view v-if="!$route.meta.keepAlive">
			          </router-view>
					</el-main>
					</el-scrollbar>
				</el-container>
			</el-container>
		</el-container>
复制代码
export default {
		created() {

		},
		data() {
			return {

			}
		},
		methods: {
			gomain() {
			//编程式导航
				this.$router.push('/homeMain')
			},
          //退出功能
			layout() {
//				退出功能要移除localStorage中的myToken
				localStorage.removeItem('myToken')
//				跳转到首页
				this.$router.push('login')
//				退出成功提示
				this.$message.success('退出成功了')
			},
			
		},
	}
复制代码

首页

  • 代码有点多截取一点,主要对echarts做了修改,x轴箭头呀,修改柱状图的样式呀等,还有element的栅格布局配合媒体查询
xAxis: {
       data: ["三月", "四月", "五月", "六月", "七月"],
		axisLine: {
			symbol: ['none', 'arrow'],
			lineStyle: {
			color: 'rgba(212,212,212,1)', // x坐标轴的轴线颜色 
				width: 1 //这里是坐标轴的宽度,为0就是不显示 
				}
            		}
        	},
        	yAxis: [{

			type: 'value',
			axisLabel: {
	    		show: false //这行代码控制着坐标轴x轴的文字是否显示 
			},
			splitLine: {
				show: false, // 网格线是否显示 
				// 改变样式 
			lineStyle: {
				color: '#EDEDED' // 修改网格线颜色 
					}
			},
			axisLine: {
				lineStyle: {
				color: '#fff', // x坐标轴的轴线颜色 
				width: 0 //这里是坐标轴的宽度,为0就是不显示 
					}
				}
		}],
复制代码

表格的使用

<div class="table">
		<div class="t-top">
			<!--使用el-input 要注意,他默认占父级100%的宽度-->
			<el-input v-model="query" placeholder="请输入内容"></el-input>
			<!--el-button 绑定点击事件向后台发送数据查询-->
			<!--在此处通常会涉及到模糊查询,此时我们还需要绑定keyup事件,向后台请求数据,然后渲染一个小的下拉框,我们需要做的是发送查询的字段给后台,
			后台使用 sql 语句模糊查询,我们渲染就可以-->
			<el-button type="primary" @click="search">查询</el-button>
		</div>
		<div class="t-bottom">
			
			    <!-- el-table:表格组件 -->
			    <!-- :data='tableData' 表格显示的数据 -->
			    <!-- el-table-column:表格的一列 -->
			    <!-- prop: 当前列要显示的数据 ,tableData内的数据-->
			    <!-- label:表头 -->
			    <!-- width: 这一列的宽度 -->
			    <!--min-width:用来设置百分比-->
			     <!--:header-cell-style="{background:'red'}"-->
		        <!--align="center"表格内的数据居中-->
			     
			 <el-table
		      :data="tableData"
		      style="width: 100%"
		      :header-cell-style="{background:'red'}"
		      >
		      <el-table-column
		        prop="date"
		        label="日期"
		        align="center"
		        width="180">
		      </el-table-column>
		      <el-table-column
		        prop="name"
		        label="姓名"
		        align="center"
		        width="180">
		      </el-table-column>
		      <!--如果不设置百分比,就自动分配剩余的宽度-->
		      <el-table-column
		        prop="address"
		        align="center"
		        label="地址">
		      </el-table-column>
		    </el-table>
		</div>
	</div>
复制代码
export default {
	  data() {
	    return {
//	      绑定的input 查询关键字
	      query: '',
//	      需要一个数组用来存放table的数据
//	      这是element组件里面的,
	        tableData: []
	    }
	  },
	  mounted() {
//	  	在vue的生命周期的mounted中调用渲染列表
	  	this.initTable()
	  },
	  methods:{
	  	initTable() {
	  		this.axios('/api/table').then( res => {
	  			console.log(res.data)//查看接口返回时什么样的数据,要常用
	  			this.tableData = res.data //接口返回的是一个数组,直接可以赋值给table
	  		})
	  	},
	  	search() {
	  		
	  	}
	  }
	}
复制代码

改element-ui的样式注意!

<!--使用scoped需要注意,使用的它之后,你就无法更改elelment组件中的样式-->
<style lang="less" scoped="scoped">
/*如果不想使用scoped,你就用父级的class把样式全部包裹起来,就不会相互影响页面的样式了*/
复制代码

以下是一些概念性的东西,其他的功能系列二将会陆续完善

编程式导航

  • 就是通过 JS 代码来实现路由的跳转功能
// 注意:是 router 不是 route
// router用来实现路由跳转,route用来获取路由参数
// push 方法的参数为:要跳转到的路由地址(path)
this.$router.push('/home')
复制代码

密码

  • 给输入框组件添加 type="password" 就变为密码框状态了
<el-input type="password" v-model="loginForm.password"></el-input>
复制代码

登录拦截

  • 说明:在没有登录的情况不应该让用户来访问除登录以外的任何页面

登录和拦截的整个流程说明

  • 1 在登录成功以后,将 token 存储到 localStorage 中
  • 2 在 导航守卫 中先判断当前访问的页面是不是登录页面
  • 3 如果是登录页面,直接放行(next())
  • 4 如果不是登录页面,就从 localStorage 中获取 token,判断有没有登录
  • 5 如果登录了,直接放行(next())
  • 6 如果没有登录,就跳转到登录页面让用户登录(next('/login'))

token 机制的说明

  • 在项目中,如果登录成功了,那么,服务器会给我们返回一个 token
  • 这个 token 就是登录成功的标识
  • 这个 token 就相当于使用 cookie+session 机制中的 sessionid

公司人员和项目开发流程

  • 1 产品经理定制项目的需求
  • 2 分配任务:先将所有的任务分配到项目组,然后,再由项目组具体分配给每个开发人员
  • 3 开发:拿到 产品原型 + 需求文档 + UI 设计稿 资料,转化为 HTML 页面,完成功能
  • 4 功能完成后,自己测试有没有 Bug
  • 5 由测试人员来测试你的功能,当测试出 Bug 后,就会通过 禅道 这样的项目管理系统,来提出 Bug
  • 6 由 自己 修改 测试人员提出来的 bug
  • 7 最终,没有 bug 了,项目才会上线
产品经理(Product Manager)
  提需求
  产出: 产品原型 + 需求文档
  原型设计软件:Axure 、墨刀

UI(设计)
  将 产品经理 给的 原型图 设计为 好看的UI稿

FE(前端)front-end
  产品原型 + 需求文档 + UI设计稿 ===> HTML页面

BE(后端) back-end
  给前端提供数据接口

测试人员
  产品原型 + 需求文档 + UI设计稿 来测试我们写的功能
  发现你的功能 与 需求 不一致,那就说明除Bug了,那么,测试人员就会提Bug
  Bug系统: 禅道

项目经理(管理技术)
  技术攻坚,与其他项目组人员沟通,分配任务 等
复制代码

vue 单文件组件中的 scoped

  • 作用:给 style 标签添加 scoped 属性以后,样式只会对当前组件中的结构生效,而不会影响到其他组件

vue 单文件组件中的 lang

lang="less"
npm i -D less less-loader

VSCode 中使用 Vetur 插件格式化单文件组件的 template

  • 打开设置,配置: "vetur.format.defaultFormatter.html": "js-beautify-html"

接口调用的说明

  • 注意: 所有接口都需要传递 token,只有传递了正确的 token,服务器才会将数据返回给前端
  • 如果没有传递 token ,服务器会返回 401 ,告诉接口调用者,没有权限来访问这个接口

cookie+session VS token

Git 使用

# 克隆仓库
git clone [仓库地址]

# 推送
git push [仓库地址] master

# 简化推送地址
git remote add XX [仓库地址]
git push -u XX master
# 第一次执行上面两条命令,以后只需要输入以下命令即可
git push XX

# 拉取
git pull [仓库地址] master
git pull XX master
复制代码

路由参数分页

  • 1 配置分页路由参数, 参数是可选的
    • 参数可选后, 路由就能够匹配: /XX 或者 /XX/3
  • 2 使用路由来分页, 有两种情况需要处理:
  • 3 第一种: 进入页面,就要根据当前路由参数中的页码,来获取到对应页的数据
  • 4 第二种: 点击分页组件获取数据, 需要做两件事:
    • 4.1 获取到当前页的数据( 调用获取数据的方法 )
    • 4.2 修改哈希值为当前页码 ( this.$router.push() )
  • 5 点击分页按钮获取数据的第二种思路:
    $route(to) {}
    

项目打包和优化

  • 打包命令: npm run build

按需加载

  • 1 修改 router/index.js 中导入组件的语法
// 使用:
const Home = () => import('@/components/home/Home')
// 替换:
// import Home from '@/components/home/Home'

// 给打包生产的JS文件起名字
const Home = () => import(/* webpackChunkName: 'home' */ '@/components/home/Home')

// chunkName相同,将 goods 和 goods-add 两个组件,打包到一起
const XX = () => import(/* webpackChunkName: 'XX' */'@/components/XX')
const XXX = () => import(/* webpackChunkName: 'XX' */'@/components/XXX')
复制代码
  • 2 ( 该步可省略 )修改 /build/webpack.prod.conf.js 中的chunkFilename
{
  // [name] 代替 [id]
  chunkFilename: utils.assetsPath('js/[name].[chunkhash].js')
}
复制代码

使用CDN

  • 开源项目 CDN 加速服务

  • 1 在 index.html 中引入CDN提供的JS文件

  • 2 在 /build/webpack.base.conf.js 中(resolve前面)添加配置 externals

  • 注意:通过CDN引入 element-ui 的样式文件后,就不需要在 main.js 中导入 element-ui 的CSS文件了。所以,直接注释掉 main.js 中的导入 element-ui 样式即可

  • externals 配置:

externals: {
  // 键:表示 导入包语法 from 后面跟着的名称
  // 值:表示 script 引入JS文件时,在全局环境中的变量名称
  vue: 'Vue',
  axios: 'axios',
  'vue-router': 'VueRouter',
  'element-ui': 'ELEMENT',
  moment: 'moment',
  echarts: 'echarts',
}

import ElementUI from 'element-ui'
复制代码

常用包CDN

<!-- Include the Quill library -->
<script src="https://cdn.bootcss.com/quill/1.3.4/quill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<!-- Quill JS Vue -->
<script src="https://cdn.jsdelivr.net/npm/vue-quill-editor@3.0.4/dist/vue-quill-editor.js"></script>
<!-- Include stylesheet -->
<link href="https://cdn.bootcss.com/quill/1.3.4/quill.core.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/quill/1.3.4/quill.snow.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/quill/1.3.4/quill.bubble.min.css" rel="stylesheet">
复制代码

移除console

new webpack.optimize.UglifyJsPlugin({
  compress:{
    warnings: false,
    drop_debugger: true,
    drop_console: true
  }
})
复制代码

缓存和保留组件状态

  • keep-alive
  • 解决方式:使用 keep-alive ,步骤如下:
1 在需要被缓存组件的路由中添加 meta 属性
  meta 属性用来给路由添加一些元信息(其实,就是一些附加信息)
{
  path: '/',
  name: 'home',
  component: Home,
  // 需要被缓存
  meta: {
    keepAlive: true
  }
}

2 修改路由出口,替换为以下形式:
  根据 meta 是否有 keepAlive 属性,决定该路由是否被缓存
<keep-alive>
  <!-- 这里是会被缓存的视图组件 -->
  <router-view v-if="$route.meta.keepAlive">
  </router-view>
</keep-alive>

<!-- 这里是不被缓存的视图组件 -->
<router-view v-if="!$route.meta.keepAlive">
</router-view>
复制代码

启用路由的 History 模式

mode: 'history'
// 去掉 # 后,地址变为:

http://localhost:1111/home

那么,服务器需要正确处理 /goods 才能正确的响应内容,
但是,/home 不是服务端的接口,而是 用来在浏览器中实现 VueRouter 路由功能的
复制代码

以上所述就是小编给大家介绍的《vue-admin 详细注释,必须手把手做项目系列之(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Software Engineering for Internet Applications

Software Engineering for Internet Applications

Eve Andersson、Philip Greenspun、Andrew Grumet / The MIT Press / 2006-03-06 / USD 35.00

After completing this self-contained course on server-based Internet applications software, students who start with only the knowledge of how to write and debug a computer program will have learned ho......一起来看看 《Software Engineering for Internet Applications》 这本书的介绍吧!

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具