目录
- 一 element-ui打包后组件显示方框解决方式
- 二 vue 解决关于*!!vue-style-loader!css-loader?{"sourceMap":true}!../../../../vue-loader类似问题的
- 三 node-sass安装
- 四 Elements in iteration expect to have 'v-bind:key' directives
- 五 el-button
- 六 表单转json
- 七 axios请求跨域问题
- 八 npm包管理
- 九 npm和yarn的区别,我们该如何选择?
- 十 { parser: “babylon” } is deprecated; we now treat it as { parser: “babel”}
- 十一 vue组件中的style scoped中遇到的问题
- 十二 JS输出内容为[object Object]
- 十三 关于Uncaught SyntaxError: Unexpected token o in JSON at position 1
- 十四 盒子模型中的布局模型
- 十五 左右两列实现
- 十六 父组件向子组件传值
- 十七 父组件向子组件传递方法
- 十八 父组件向子组件传递事件/调用事件
一 element-ui打包后组件显示方框解决方式
本节内容来自这里。
在我们对vue项目进行打包的时候,会遇到一些组件问题,比如说分页组件的箭头,下拉框组件的箭头打包完成后会出现方框显示的问题.具体解决办法如下:
1 找到项目底下build/utils.js文件
2 添加 publicPath: ‘…/…/’
二 vue 解决关于*!!vue-style-loader!css-loader?{“sourceMap”:true}!../…/…/…/vue-loader类似问题的
本节内容来自这里。
如果是 sass 的,执行 npm install sass sass-loader --save-dev 安装依赖就行。或者($npm intall sass-loader --save ; $npm install node-sass --save)
三 node-sass安装
由于npm会限制此包,所有可以使用cnpm:
cnpm install node-sass
四 Elements in iteration expect to have ‘v-bind:key’ directives
<div class="line-item" v-for="(item, index) in productList" :key="index">
写for循环的时候,一定要加key,不加key会报错。
五 el-button
本节内容来自这里。
六 表单转json
console.log(JSON.stringify(this.inputForm))
七 axios请求跨域问题
本节内容来自这里。
背景:因为axios中只能使用get和post方法来进行请求数据,没有提供jsonp等方法进行跨域访问数据。
// axios中的GET请求
axios.get('/user', {
params: {
ID: ‘001’
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// axios中的POST请求
axios.post('/user', {
firstName: '1',
lastName: '2'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
方案1
使用axios直接进行跨域访问不可行,就需要配置代理。原因:因为客户端请求服务端的数据是存在跨域问题的,而服务器和服务器之间可以相互请求数据,是没有跨域的概念(如果服务器没有设置禁止跨域的权限问题),配置一个代理的服务器可以请求另一个服务器中的数据,代理服务器再返回数据给客户端。
准备工作:安装所需中间件和插件等,比如axios,http-proxy-middleware等。
具体案例:这里以访问豆瓣Top250为例,直接访问如下:
axios.get("http://api.douban.com/v2/movie/top250")
.then(res=>{
console.log(res)
})
.catch(err=>{
console.log(err)
})
当执行npm run dev
时,控制台报错如下:
1、配置BaseUrl
在main.js中,配置数据所在服务器的前缀(即固定部分),代码如下:
// 项目入口,配置全局vue
import Vue from 'vue'
import VueRouter from './router/routes.js'
import Store from './store/index.js'
import './assets/less/index.less'
import App from './App.vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-default/index.css'
import axios from 'axios'
Vue.prototype.$axios = axios
axios.defaults.baseURL = '/api' //关键代码
Vue.config.productionTip = false
Vue.use(ElementUI);
new Vue({
router:VueRouter,
store:Store,
template:'<App/>',
components: {App}
}).$mount('#app')
// 默认进入商品模块
// VueRouter.push({ path: '/home' })
关键代码:axios.defaults.baseURL = '/api'
,作用是我们每次发送的请求都会带一个/api
的前缀。
2、配置代理
在config文件夹下的index.js文件中的proxyTable字段中,作如下处理:
dev: {
env: require('./dev.env'),
port: 8090,
autoOpenBrowser: true,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api': {
target:'http://api.douban.com/v2', // 你请求的第三方接口
changeOrigin:true, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite:{ // 路径重写,
'^/api': '' // 替换target中的请求地址,也就是说以后你在请求http://api.douban.com/v2/XXXXX这个地址的时候直接写成/api即可。
}
}
},
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
cssSourceMap: false
}
3、在具体使用axios的地方,修改url如下即可:
axios.get("/movie/top250")
.then((res) => {
res = res.data
if (res.errno === ERR_OK) {
this.themeList=res.data;
}
})
.catch((error) => {
console.warn(error)
})
4、重新启动项目之后,已经解决了跨域问题,结果如下:
5、区分一下生产环境和开发环境,集体配置如下:
在config文件夹里面创建一个api.config.js的配置文件
const isPro = Object.is(process.env.NODE_ENV, 'production')
console.log(isPro);
module.exports = {
baseUrl: isPro ? 'https://www.***/index.php/Official(线上地址)' : 'api/'
}
在main.js文件里面引入上面文件,这样就可以保证动态的匹配生产和开发环境的定义前缀了,代码如下:
import Vue from 'vue'
import App from './App'
import router from './router'
import 'bootstrap/dist/js/bootstrap.min'
import 'bootstrap/dist/css/bootstrap.min.css'
import axios from 'axios'
import apiConfig from '../config/api.config'
Vue.prototype.$axios = axios;
Vue.config.productionTip = false;
axios.defaults.baseURL = apiConfig.baseUrl;// 配置接口地址
axios.defaults.withCredentials = false;
以上两步即可解决vue的跨域问题,并且可以可以直接build打包到线上。
原理:
因为我们给url加上了前缀/api,我们访问/movie/top250就当于访问了:localhost:8080/api/movie/top250(其中localhost:8080是默认的IP和端口)。
在index.js中的proxyTable中拦截了/api,并把/api及其前面的所有替换成了target中的内容,因此实际访问Url是http://api.douban.com/v2/movie/top250。
至此,纯前端配置代理解决axios跨域得到解决。
方案2
后端处理跨域问题,加个过滤器即可解决,如下:
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
* 跨域过滤器
* @author jitwxs
* @since 2018/10/16 20:53
*/
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
// 不使用*,自动适配跨域域名,避免携带Cookie时失效
String origin = request.getHeader("Origin");
if(StringUtils.isNotBlank(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
}
// 自适应所有自定义头
String headers = request.getHeader("Access-Control-Request-Headers");
if(StringUtils.isNotBlank(headers)) {
response.setHeader("Access-Control-Allow-Headers", headers);
response.setHeader("Access-Control-Expose-Headers", headers);
}
// 允许跨域的请求方法类型
response.setHeader("Access-Control-Allow-Methods", "*");
// 预检命令(OPTIONS)缓存时间,单位:秒
response.setHeader("Access-Control-Max-Age", "3600");
// 明确许可客户端发送Cookie,不允许删除字段即可
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
/*
注册过滤器:
@Bean
public FilterRegistrationBean registerFilter() {
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>();
bean.addUrlPatterns("/*");
bean.setFilter(new CorsFilter());
// 过滤顺序,从小到大依次过滤
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
*/
}
八 npm包管理
1 NPM查看包的版本信息
分两种情况,NPM命令可以查看npmjs服务器上的包的版本信息,也可以查看本地安装的包的版本信息。
查看npmjs服务器上的包的版本信息
1、npm view pkg version
查看服务器上包pkg的最新的版本信息。
npm view gulp version
4.0.0
2、npm view pgk versions
查看npmjs服务器上包pkg的所有的版本信息。
npm view gulp versions
3、npm info pkg
查看npmjs服务器上包pkg的最新的版本信息,和 npm view pkg version
的功能类似,但比 npm view pkg version
提供的信息更丰富。
npm info gulp
查看本地安装包的版本信息
1、npm ls pkg
查看某个项目下包 pkg 的版本信息,注意该命令需要在某个项目下执行。
npm ls webpack
webapp_backend@1.0.0 /本地的项目路径
└── webpack@3.12.0
2、npm ls pkg -g 查看本地全局安装的pkg版本。
npm ls webpack -g
/Users/mac/.nvm/versions/node/v8.8.0/lib
└── webpack@4.29.6
2 模式
运行webpack命令时,一定要指定模式。
webpack --mode development
webpack --mode production
3 --save -dev
--save
:将保存配置信息到pacjage.json。默认为dependencies节点中。
--dev
:将保存配置信息devDependencies节点中。
因此:
--save
:将保存配置信息到pacjage.json的dependencies节点中。
--save-dev
:将保存配置信息到pacjage.json的devDependencies节点中。
dependencies
:运行时的依赖,发布后,即生产环境下还需要用的模块
devDependencies
:开发时的依赖。里面的模块是开发时用的,发布时用不到它。
4 npm中安装固定的版本号package,只需要在其后加 ‘@版本号’
npm install --save esri-loader@1.0.0
5 其他命令
npm install [packageName] //安装模块
npm install [packageName]@xxx.xx //安装模块的指定版本
npm install [packageName] -g //全局安装模块
npm install [packageName] --save 安装好后写入package.json的dependencies中(生产环境依赖)
npm install [packageName] --save-dev 安装好后写入package.json的devDepencies中(开发环境依赖)
npm uninstall [packageName] // 删除模块
npm uninstall [packageName] -g //卸载全局模块
npm uninstall [packageName] --save // 删除模块,同时删除模块留在package.json中dependencies下的对应信息
npm uninstall [packageName] --save-dev // 删除模块,同时删除模块留在package.json中devDependencies下的对应信
九 npm和yarn的区别,我们该如何选择?
1 Yarn是什么?
“Yarn是由Facebook、Google、Exponent 和 Tilde 联合推出了一个新的 JS 包管理工具 ,正如官方文档中写的,Yarn 是为了弥补 npm 的一些缺陷而出现的。”这句话让我想起了使用npm时的坑了:
npm install的时候巨慢。特别是新的项目拉下来要等半天,删除node_modules,重新install的时候依旧如此。
同一个项目,安装的时候无法保持一致性。由于package.json文件中版本号的特点,下面三个版本号在安装的时候代表不同的含义。
5.0.3
,~5.0.3
,^5.0.3
;5.0.3
表示安装指定的5.0.3版本,~5.0.3
表示安装5.0.X中最新的版本,^5.0.3
表示安装5.X.X中最新的版本。这就麻烦了,常常会出现同一个项目,有的同事是OK的,有的同事会由于安装的版本不一致出现bug。
安装的时候,包会在同一时间下载和安装,中途某个时候,一个包抛出了一个错误,但是npm会继续下载和安装包。因为npm会把所有的日志输出到终端,有关错误包的错误信息就会在一大堆npm打印的警告中丢失掉,并且你甚至永远不会注意到实际发生的错误。
带着这些坑,我开始了解Yarn的优势及其解决的问题。
2 Yarn的优点?
速度快 。速度快主要来自以下两个方面:
并行安装:无论 npm 还是 Yarn 在执行包的安装时,都会执行一系列任务。npm 是按照队列执行每个 package,也就是说必须要等到当前 package 安装完成之后,才能继续后面的安装。而 Yarn 是同步执行所有任务,提高了性能。
离线模式:如果之前已经安装过一个软件包,用Yarn再次安装时之间从缓存中获取,就不用像npm那样再从网络下载了。
安装版本统一:为了防止拉取到不同的版本,Yarn 有一个锁定文件 (lock file) 记录了被确切安装上的模块的版本号。每次只要新增了一个模块,Yarn 就会创建(或更新)yarn.lock 这个文件。这么做就保证了,每一次拉取同一个项目依赖时,使用的都是一样的模块版本。npm 其实也有办法实现处处使用相同版本的 packages,但需要开发者执行 npm shrinkwrap 命令。这个命令将会生成一个锁定文件,在执行 npm install 的时候,该锁定文件会先被读取,和 Yarn 读取 yarn.lock 文件一个道理。npm 和 Yarn 两者的不同之处在于,Yarn 默认会生成这样的锁定文件,而 npm 要通过 shrinkwrap 命令生成 npm-shrinkwrap.json 文件,只有当这个文件存在的时候,packages 版本信息才会被记录和更新。
更简洁的输出:npm 的输出信息比较冗长。在执行 npm install 的时候,命令行里会不断地打印出所有被安装上的依赖。相比之下,Yarn 简洁太多:默认情况下,结合了 emoji直观且直接地打印出必要的信息,也提供了一些命令供开发者查询额外的安装信息。
多注册来源处理:所有的依赖包,不管他被不同的库间接关联引用多少次,安装这个包时,只会从一个注册来源去装,要么是 npm 要么是 bower, 防止出现混乱不一致。
更好的语义化: yarn改变了一些npm命令的名称,比如 yarn add/remove,感觉上比 npm 原本的 install/uninstall 要更清晰。
3 Yarn和npm命令对比
npm | yarn |
---|---|
npm install | yarn |
npm install react --save | yarn add react |
npm uninstall react --save | yarn remove react |
npm install react --save-dev | yarn add react --dev |
npm update --save | yarn upgrade |
4 npm的未来:npm5.0
有了yarn的压力之后,npm做了一些类似的改进。
默认新增了类似yarn.lock的 package-lock.json;
git 依赖支持优化:这个特性在需要安装大量内部项目(例如在没有自建源的内网开发),或需要使用某些依赖的未发布版本时很有用。在这之前可能需要使用指定 commit_id 的方式来控制版本。
文件依赖优化:在之前的版本,如果将本地目录作为依赖来安装,将会把文件目录作为副本拷贝到 node_modules 中。而在 npm5 中,将改为使用创建 symlinks 的方式来实现(使用本地 tarball 包除外),而不再执行文件拷贝。这将会提升安装速度。目前yarn还不支持。
5 总结
在 npm5.0
之前,yarn
的优势特别明显。但是在 npm5
之后,通过以上一系列对比,我们可以看到 npm5
在速度和使用上确实有了很大提升,值得尝试,不过还没有超过 yarn
。
综上我个人的建议是如果你已经在个人项目上使用 yarn
,并且没有遇到更多问题,目前完全可以继续使用。但如果有兼容 npm
的场景,或者身处在使用 npm
,cnpm
,tnpm
的团队,以及还没有切到 yarn
的项目,那现在就可以试一试 npm5
了。
十 { parser: “babylon” } is deprecated; we now treat it as { parser: “babel”}
webpack 创建 vue 工程以后,运行项目后控制台提示{ parser: “babylon” } is deprecated; we now treat it as { parser: “babel” }.
。
解决方法:找到工程里面文件夹:node_modules\vue-loader\lib\template-compiler\index.js,将以下代码:
if (!isProduction) {
code = prettier.format(code, { semi: false, parser: 'babylon' })
}
修改为:
if (!isProduction) {
code = prettier.format(code, { semi: false, parser: 'babel' })
再次运行项目 npm run dev
即可。
十一 vue组件中的style scoped中遇到的问题
在uve组件中我们我们经常需要给style添加scoped来使得当前样式只作用于当前组件的节点。添加scoped之后,实际上vue在背后做的工作是将当前组件的节点添加一个像data-v-1233这样唯一属性的标识,当然也会给当前style的所有样式添加[data-v-1233]这样的话,就可以使得当前样式只作用于当前组件的节点。但是我们需要注意的是如果我们添加了子组件,同样的,如果子组件也用scoped标识了,那么在父组件中是不能设置子组件中的节点的。若父组件有scoped,子组件没有设置,同样,也是不能在父组件中设置子组件的节点的样式的,因为父组件用了scoped,那么父组件中style设置的样式都是唯一的了,不会作用与其他的组件样式。
十二 JS输出内容为[object Object]
本节内容来自这里。
问题描述
项目中,欲在控制台输出变量res(自定义对象)查看数据,代码为:
console.log(‘res:’ + res);
但控制台显示结果为res: [object Object],并非想要查看的数据。
问题原因
-
加号的作用
首先,我们的代码中有+(加号)运算符,它在这种情况下(字符串 + 其它什么东西),会调用toString()方法,将其它类型的东西转化为字符串,再和原始字符串拼接成一个字符串; -
toString()从哪里来,干了什么?
以下摘自MDN:
除了null和undefined之外,其他的类型(数值、布尔、字符串、对象)都有toString()方法,它返回相应值的字符串表现(并不修改原变量)。
每个对象都有一个toString()方法。
当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。
默认情况下,toString()方法被每个Object对象继承。如果此方法在自定义对象中未被覆盖,toString()返回 “[object type]”,其中type是对象的类型。
而在上述项目中,res正是我们自定义的对象,所以res.toString()的结果为[object Object],所以console.log(‘res:’ + res)的结果为res: [object Object]。
解决方法
去掉字符串‘res’,直接输出对象res
代码改写如下:
console.log(JSON.stringify(user))
Tips:一些补充
(1).toString() // “1”
[1,2].toString() // “1, 2”
({}).toString() // [object Object]
true.toString() // “true”
null.toString() // Uncaught TypeError: Cannot read property ‘toString’ of null
undefined.toString() // Uncaught TypeError: Cannot read property ‘toString’ of null
第3行,不能直接写成{}.toString()是因为:{}会被当成代码块而不是空对象
十三 关于Uncaught SyntaxError: Unexpected token o in JSON at position 1
本节内容来自这里。
Uncaught SyntaxError: Unexpected token o in JSON at position 1是一个在使用JSON.parse方法时经常会遇到的报错,
那么问题来了报错信息中的token o代表了啥???
首先必须记住,JSON.parse的第一个参数是字符串!!!所以上面的转化自然是[object object]
测试一下其他数据就知道了
切记我们在使用JSON.parse方法的时候,请先用JSON.stringify方法
JSON.parse(JSON.stringify(data))
有用的链接
mdn呀
https://stackoverflow.com/questions/38380462/syntaxerror-unexpected-token-o-in-json-at-position-1
十四 盒子模型中的布局模型
1 基本概念
CSS包含3种基本的布局模型:流动模型(Flow)、浮动模型 (Float) 和层模型(Layer)。
2 流动模型(Flow)
流动(Flow)是默认的网页布局模式。也就是说网页在默认状态下的 HTML 网页元素都是根据流动模型来分布网页内容的。
流动布局模型具有2个比较典型的特征:
第一点,块状元素都会在所处的包含元素内自上而下按顺序垂直延伸分布,因为在默认状态下,块状元素的宽度都为100%。实际上,块状元素都会以行的形式占据位置。
第二点,内联元素都会在所处的包含元素内从左到右水平分布显示。(内联元素可不像块状元素这么霸道独占一行)(非常类似Linearlayout里面的垂直和水平的属性)。
3 浮动模型(Float)
将块状元素定义为浮动就可使其在一行显示出。
<!-- 左浮动 -->
div {border:2px red solid; float:left;}
<div id="div1">左1</div>
<div id="div2">左2</div>
<!-- 右浮动 -->
div {border:2px red solid; float:right;}
<div id="div1">右1</div>
<div id="div2">右2</div>
<!-- 一左一右 -->
#div1 {float:left;}
#div2 {float:right;}
<div id="div1">左1</div>
<div id="div2">右1</div>
4 层模型(Layer)
如何让html元素在网页中精确定位,就像图像软件PhotoShop中的图层一样可以对每个图层能够精确定位操作。CSS定义了一组定位(positioning)属性来支持层布局模型。
层模型有三种形式: 绝对定位(position:absolute)、相对定位(position:relative)、固定定位(position:fixed)
绝对定位(position:absolute)
需要设置position:absolute(表示绝对定位),这条语句的作用将元素从文档流中拖出来,然后选定一个最接近该元素的并且具有定位属性的父控件作为定位参照物, 如果不存在这样的包含块,则相对于body元素,即相对于浏览器窗口。选定好定位的参照物之后,设定left、right、top、bottom的值进行定位,注意例如 left:20px; 是说本身元素定位的地址是元素左边距离参照物为20px.而不是向左移动20px。
相对定位(position:relative)
需要设置 position:relative(表示相对定位),它通过left、right、top、bottom属性确定元素在正常文档流中的偏移位置。
相对定位完成的过程是首先按static(float)方式生成一个元素(并且元素像层一样浮动了起来),然后选定以前的位置为定位参照物,left、right、top、bottom属性确定和以前的位置的距离。
注意:偏移前的位置保留不动,这句话的意思,你可以把相对定位想象成他又分身了一下,把之前的位置保留但是隐藏了自己,新的分身根据方向和距离进行新的定位。所以隐藏了自己的人,他还是存在该文档中,所以他后面的元素还是照常显示,只不过他仅仅隐藏了自己。
固定定位(position:fixed)
fixed定位与 absolute 定位类型类似,但它的相对移动的坐标是视图(屏幕内的网页窗口)本身。由于视图本身是固定的,它不会随浏览器窗口的滚动条滚动而变化,除非你在屏幕中移动浏览器窗口的屏幕位置,或改变浏览器窗口的显示大小,因此固定定位的元素会始终位于浏览器窗口内视图的某个位置,不会受文档流动影响,这与 background-attachment:fixed
;属性功能相同。(例如: 直达顶部的按钮)
position:absolute
与 position:relative
结合实现相对于父辈元素进行绝对定位,需要遵循以下规范:
1、参照定位的元素必须是相对定位元素的前辈元素:
<div id="box1"><!--参照定位的元素-->
<div id="box2">相对参照元素进行定位</div><!--相对定位元素-->
</div>
从上面代码可以看出 box1 是 box2 的父元素(父元素当然也是前辈元素了)。
2、参照定位的元素必须加入 position:relative;
#box1{
width:200px;
height:200px;
position:relative;
}
3、定位元素加入 position:absolute,便可以使用 top、bottom、left、right 来进行偏移定位了。
#box2{
position:absolute; top:20px; left:30px;
}
这样 box2 就可以相对于父元素 box1 定位了(这里注意参照物就可以不是浏览器了,而可以自由设置了)。
十五 左右两列实现
1 任务描述
实现左右拖拽布局功能:当拖动分割线时,可以重置左右两侧模块宽度实现宽度自定义。
实现在 vue 中上下拖动改变 div 宽高,本文应用场景为 div 中使用 <svg> 画一些图标。
<div id="svgBox">
<div id="svgTop">
<svg width="100%" id="controllerSvg"></svg>
</div>
<div id="svgResize"></div>
<div id="svgDown" style="border-top: 1px solid #b5b9a9; ">
<svg width="100%" height="auto" id="serverSvg"></svg>
</div>
</div>
2 整体思路
(1)按下鼠标时,获取当前点击位置距离当前 body 的位置;
(2)移动该鼠标时,计算左边 div 的距离,计算公式是 = 原先左边div的距离 + 最后鼠标的位置 - 起始鼠标的位置;
(3)在容错处理上,设置了可移动的最大距离,防止宽度溢出。
总体结构分为三部分,上 div,分界线,下 div。然后把这三个 div 放在一个大的 div 里面。
这四个 div 的 css 样式如下:
#svgBox{
width:100%;
height:100%;
position: relative;
overflow:hidden;
}
#svgTop{
height:calc(30% - 5px);
width:100%;
float:left;
overflow: auto;
}
#svgResize{
position: relative;
height:5px;
width:100%;
cursor: s-resize;
float:left;
}
#svgDown{
height:70%;
width:100%;
float:left;
overflow: hidden;
}
然后在该页面中编写逻辑代码,我用的框架是vue,先将其写成一个 method,然后在 mounted()
生命周期里面调用该方法。
dragControllerDiv : function () {
$(document).ready(function(){
var svgResize = document.getElementById("svgResize");
var svgTop = document.getElementById("svgTop");
var svgDown = document.getElementById("svgDown");
var svgBox = document.getElementById("svgBox");
svgResize.onmousedown = function(e){
var startY = e.clientY;
svgResize.top = svgResize.offsetTop;
document.onmousemove = function(e){
var endY = e.clientY;
var moveLen = svgResize.top + (endY - startY);
var maxT = svgBox.clientHeight - svgResize.offsetHeight;
if(moveLen<30) moveLen = 30;
if(moveLen>maxT-30) moveLen = maxT-30;
svgResize.style.top = moveLen;
svgTop.style.height = moveLen + "px";
svgDown.style.height = (svgBox.clientHeight - moveLen - 5) + "px";
}
document.onmouseup = function(evt){
document.onmousemove = null;
document.onmouseup = null;
svgResize.releaseCapture && svgResize.releaseCapture();
}
svgResize.setCapture && svgResize.setCapture();
return false;
}}
)
},
this.dragControllerDiv();
这里面要注意的就是,e.clientY/offsetTop
这些属性,都是获取的哪个对象的值,然后根据自己的页面实际调整。结合上面温习的盒子模型,搞清楚层模型中的定位的用法。
发现问题以及反馈:
整个 #svgBox
的高度未固定住,当上下拖动拖动条时,#svgDown
的高度没变,反而是 #svgBox
的高度在变化。和我们想要的效果恰恰相反,我们预期效果应该是把 #svgBox
高度固定,拖动的时候,#svgTop
和 #svgDown
的高度随之变化。
更正这个bug,我们根据屏幕分辨率不同,获取到当前屏幕的高度,然后把屏幕高度的40%赋值给 #svgBox
的高度。代码如下:
var h = window.screen.height;
console.log(h);
var svgBox = document.getElementById("svgBox");
svgBox.style.height = h * 0.4 + "px";
此时就可以将 #svgBox
的高度固定住。
3 完整代码
<!-- left:相对于具有定位属性(position定义为relative)的父对象的左边距;-->
<!-- offsetLeft:当前元素距父元素左侧的值;-->
<!-- offsetWidth:元素的width+元素的padding+边框的宽度。-->
<template>
<div id="box">
<div class="top">顶部导航</div>
<div id="left"> 左边的div
<svg width="100%" id="controllerSvg" ></svg>
</div>
<div id="resize"></div>
<div id="right" style="border-top: 1px solid #b5b9a9; ">右边的div
</div>
<p></p>
</div>
</template>
<script>
export default {
name: 'change',
methods: {
dragControllerDiv: function () {
let resize = document.getElementById('resize')
let left = document.getElementById('left')
let right = document.getElementById('right')
let box = document.getElementById('box')
resize.onmousedown = function (e) {
let startX = e.clientX
resize.left = resize.offsetLeft
document.onmousemove = function (e) {
let endX = e.clientX
let moveLen = resize.left + (endX - startX)
let maxT = box.clientWidth - resize.offsetWidth
if (moveLen < 150) moveLen = 360
if (moveLen > maxT - 800) moveLen = maxT - 800
resize.style.left = moveLen
left.style.width = moveLen + 'px'
right.style.width = (box.clientWidth - moveLen - 5) + 'px'
}
document.onmouseup = function () {
document.onmousemove = null
document.onmouseup = null
resize.releaseCapture && resize.releaseCapture()
}
resize.setCapture && resize.setCapture()
return false
}
}
},
mounted () {
this.dragControllerDiv()
}
}
</script>
<style scoped>
#box{
width:100%;
height:400px;
position: relative;
overflow:hidden;
}
.top {
width: 100%;
height: 80px;
background: #ffe0c6;
}
#left{
width:calc(30% - 5px);
height: 100%;
float:left;
overflow: auto;
background: pink;
}
#resize{
position: relative;
width:5px;
height:100%;
cursor: w-resize;
float:left;
}
#right{
width:70%;
height:100%;
float:left;
overflow: hidden;
background: #ffc5c1;
}
</style>
4 参考文章
Vue—拖动侧边栏改变div宽度:https://blog.csdn.net/qq_39295665/article/details/99622493
vue中实现左右,上下拖动,改变div宽高:https://www.jianshu.com/p/eb33a803c106
十六 父组件向子组件传值
1、父组件如何将值传给子组件?
2、子组件如何获取父组件传递过来的值?
示例代码:
<div id='app'>
<com1 :parentmsg='msg'></com1>
</div>
var vm = new Vue({
el:'#app',
data:{
msg: '这是父组件中的数据'
},
methods:{},
//定义子组件
components:{
com1:{
data(){
return {title:'子组件',content:'这是子组件内容'}
},
template:'<h1>这是子组件--{{parentmsg}}</h1>',
props:['parentmsg']
}
}
})
解读vue示例代码:
1、父组件向子组件传值是通过“属性绑定”的形式实现的(第2行);
2、子组件默认是无法访问到父组件中的数据和方法的(如果去掉18行,17行是取不到数据的,浏览器还会报错);
过程分析:
1、首先,第2行是引用子组件对象模板,通过属性绑定的方式将父组件的 msg 绑定给自定义的属性名称 parentmsg,这一步已经把父组件的值传给了子组件;
2、看代码 vue 中是通过在子组件中定义 props 属性,并将绑定的属性名传进去就可以获取到父组件传过来的值。
关键字:属性绑定 、 props
注意:
1、子组件 data 里面的数据是子组件私有的,可读可写。
2、组建中 props 种的数据都是通过父组件传递过来的,可读不可写。
十七 父组件向子组件传递方法
<div id='app'>
<com2 @func='show'></com2>
</div>
<template id='tem1'>
<div>
<h1>这是子组件</h1>
<input type="button" value="子组件的按钮,点击调用父组件传递过来的func方法" @click='myclick'>
</div>
</template>
//定义一个字面量类型的组件模板对象
var com2 = {
tenplate:'#tem1',
data(){
return{
sonmsg:{name:'son',age:6}
}
},
methods:{
myclick(){
this.$emit('func’,this.sonmsg);
}
}
}
var vm = new Vue({
el:'#app',
data:{
data_form_son: ''
},
methods:{
show(param){
console.log("调用父组件的show方法"+JSON.stringify(param));
this.data_form_son = JSON.stringify(param);
}
},
components:{
com2
}
})
该问题可以理解为“子组件如何调用父组件的方法”,根据对父组件向子组件传值过程的理解,该过程还是分为两个步骤:
1、父组件如何将方法传给子组件?
2、子组件如何获取子组件传过来的方法?
解读 vue 示例代码:
1、父组件通过事件绑定机制”将方法传给子组件。
2、子组件在 methods 属性中用 this.$emit('func')
获取父组件传递的方法。
过程分析:
1、首先在定义的 vue 对象实例中定义一个 show 方法(第32行),然后我们通过 components 属性将定义的子组件 com2 引用一下,在第2行以标签的形式加以引用,在标签里面用事件绑定机制将 show 绑定给 func,此时已成功将父组件的 show 方法传递给了子组件 com2;
2、我们需要触发并调用父组件传递过来的方法,在子组件 com2 的 methods 属性中定义了点击事件方法,点击的时候通过 this.$emit('func',this,sonmsg)
获取父组件的方法。
注意:
1、@func='show'
不是 show(),表示将 show() 的引用直接给 func,如果是 show() 表示将方法的结果给 func,注意含义不一样。
2、this.emit()
,emit 是触发的意思,第一个参数是父组件传递的方法名,从第二个参数开始可以传递额外的参数。
十八 父组件向子组件传递事件/调用事件
父组件app.vue
<template>
<div id="app">
<!--父组件-->
<input v-model="msg" />
<button v-on:click="notify">广播事件</button>
<!--子组件-->
<popup ref="child"></popup>
</div>
</template>
<script>
import popup from "@/components/popup";
export default {
name: "app",
data: function () {
return {
msg: "",
};
},
components: {
popup,
},
methods: {
notify: function () {
if (this.msg.trim()) {
this.$refs.child.parentMsg(this.msg);
}
},
},
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
子组件popup.vue
<template>
<div>
<ul>
<li v-for="item in messages">父组件输入了:{{item}}</li>
</ul>
</div>
</template>
<style>
body {
background-color: #ffffff;
}
</style>
<script>
export default {
name: "popup",
data: function () {
return {
messages: [],
};
},
methods: {
parentMsg: function (msg) {
this.messages.push(msg);
},
},
};
</script>