豆芽丝 - Javascript https://douyasi.com/category/javascript zh-CN javascript相关 Sat, 14 Jul 2018 00:43:00 +0800 Sat, 14 Jul 2018 00:43:00 +0800 Vue 实现父子组件双向绑定最正确的做法 https://douyasi.com/javascript/vue-bindings.html https://douyasi.com/javascript/vue-bindings.html Sat, 14 Jul 2018 00:43:00 +0800 douyasi 最近在学习 vue ,遇到了父子组件数据绑定的问题。Vue 官方文档有这样一段话:

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

但这并不是说父页面就不能跟子组件双向通讯,不过就是麻烦了一点。在 Google 了诸多之后,尝试了网络上一些破碎零星的代码(什么计算属性,侦听器等)之后,终于找到了最佳的做法:prop.sync

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Prop 双向绑定的实现</title>
    <script src="dist/vue.js"></script>
</head>
<body>
<div id="app">
    <h1>父组件数据</h1>
    <table>
        <tr>
            <th>姓名</th>
            <th>年龄</th>
        </tr>
        <tr>
            <td>{{ name }} <input type="text" v-model="name" /></td>
            <td>{{ age }} <input type="number" v-model.number="age" /></td>
        </tr>
    </table>
    <!--
      v-bind:my-name
      v-on:update:my-name="my-name = $event"
    参考文档
      https://cn.vuejs.org/v2/guide/components-custom-events.html
    -->
    <user-table
      v-bind:my-name.sync="name"
      v-bind:my-age="age"
      v-on:change-age="age = $event"
    ></user-table>
</div>
<template id="userTable">
    <div>
        <h2>子组件数据</h2>
        <table>
            <tr>
                <th>姓名</th>
                <th>年龄</th>
            </tr>
            <tr>
                <td>{{ myName }} <input type="text" :value="myName" @input="updateName" /></td>
                <td>{{ myAge }} <input type="number" :value="myAge" @input="updateAge" /></td>
            </tr>
        </table>
    </div>
</template>
<style>
table, td, th {
    border-collapse: collapse;
    border-spacing: 0
}
table {
    margin: 20px;
}
td, th {
    border: 1px solid #bcbcbc;
    padding: 5px 10px
}
th {
    background: #42b983;
    font-weight: 400;
    color: #fff;
    cursor: pointer
}
</style>
<script>
var UserTable = {
  props: {
    myName: String,
    myAge: Number
  },
  template: '#userTable',
  watch: {
    myName: function (val) {
      console.log('child-component watch fater-component name:' + val)
    },
    myAge: function (val) {
      console.log('child-component watch father-component age:' + val)
    }
  },
  methods: {
    updateName (evt) {
      console.log(evt)
      console.log('_name value:' + this.myName)
      console.log('evt.target.value:' + evt.target.value)
      this.$emit('update:myName', evt.target.value)
      console.log('child-component myName:' + this.myName)
    },
    updateAge (evt) {
      console.log(evt)
      console.log('_name value:' + this.myAge)
      console.log('evt.target.value:' + evt.target.value)
      // 自定义 change-age 事件
      this.$emit('change-age', Number(evt.target.value))
      console.log('child-component myAge:' + this.myAge)
    }
  }
}
new Vue({
  el: '#app',
  data: {
    name: '张三',
    age: 20
  },
  components: {
    'user-table': UserTable
  },
  mounted() {
    const vm = this
    setInterval(() => { console.log('name', this.name, 'age', this.age); }, 3000);
  }
})
</script>
</body>
</html>

使用 Event-Emit 方式实现的示例(请随意在输入框中输入数据,观察数据文本变化):

https://raoyc.com/learning_vue/example/cp_2.html

传送门:

vue 学习笔记仓库

参考文档和资料:

官方文档:自定义事件
总结性比较好的一篇博文:Vue2.x 中实现父子组件间的双向绑定

]]>
0 https://douyasi.com/javascript/vue-bindings.html#comments https://douyasi.com/feed/javascript/vue-bindings.html
MarkdocViewer https://douyasi.com/javascript/markdoc-viewer.html https://douyasi.com/javascript/markdoc-viewer.html Sat, 20 Aug 2016 17:17:00 +0800 douyasi 最近闲来无聊造了个文档阅读的东西,就一个单页,欢迎使用。

传送门:https://github.com/ycrao/markdoc-viewer

MarkdocViewer : reading markdown documentation from open git repository (just in one page) !

MarkdocViewer :从公开的 git 仓库阅读 markdown 文档 (就一个页面)!

参数配置

MarkdocViewer默认配置如下:

// default options
{
    // git repo raw file base_url, default to github
    'base_url': 'https://raw.githubusercontent.com/',
    // document directory, default empty
    'base_dir': '',
    // git repo name, default 'yascmf/docs'
    'repo_name': 'ycrao/mynotes',
    // git branch name, default 'master'
    'branch_name': 'master',
    // index file name, using it to generate sider -nav menu
    'index_file': 'index.md',
    // home file name, default 'README.md'
    'home_file': 'intro.md',
    // element id for left-sider or right-sider menu in HTML
    'sider_id': 'sider-menu',
    // element id for main content in HTML
    'content_id': 'content',
}

以上某些配置,可以通过传入查询串,予以重载新配置:

https://raoyc.com/markdoc-viewer?doc=intro.md&dir=&index=index.md&repo=ycrao/mynotes&home=intro.md&branch=master

其中,dir 查询串对应 base_dirrepo 对应 repo_name 配置,后面依次类推。请保证,查询串值与仓库文档实际结构目录一致。

const dir = config("dir", ""),
    repo = config("repo", "ycrao/mynotes"),
    branch = config("branch", "master"),
    index = config("index", "index.md"),
    home = config("home", "intro.md");
const options = {
    base_url: "https://raw.githubusercontent.com/",
    base_dir: dir,
    repo_name: repo,
    branch_name: branch,
    index_file: index,
    home_file: home,
    sider_id: "sider-menu",
    content_id: "content",
};
console.log(config);
var mv = new MarkdocViewer(options);
mv.viewer();

在线文档阅读示例

鸣谢

本源码使用到以下开源组件:

]]>
0 https://douyasi.com/javascript/markdoc-viewer.html#comments https://douyasi.com/feed/javascript/markdoc-viewer.html
json对象的遍历 https://douyasi.com/javascript/json_foreach.html https://douyasi.com/javascript/json_foreach.html Tue, 03 Feb 2015 14:53:00 +0800 douyasi 在写内容管理框架时,需要实现根据当前路由 url 自动对导航栏特定项目予以高亮。由于,原有 cmf 是使用 json 结合 laytpl 模版引擎生成导航栏。

这个过程中需要使用到 json 对象遍历,下面简单学习下对如何对json数据遍历。
这里主要使用到 for ( var d in data ) 语句,对于复杂结构的 json 数据比较好用。

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>route_map</title>
</head>
<body>
    <script type="text/javascript">
//假设你得到了这么一段数据
var data = {
    desc: '导航区域',
    list: [
        {
            index: 0,
            anchor: 'admin.console.index', icon: 'fa-home', title: '控制台', 
            submenu: [
                {link: 'admin.console.index', name: '概要'}
            ]
        },
        {
            index: 1,
            anchor: 'admin.article.index', icon: 'fa-edit', title: '内容管理', 
            submenu: [
                {link: 'admin.article.index', name: '文章'},
                {link: 'admin.page.index', name: '单页'},
                {link: 'admin.fragment.index', name: '碎片'},
                {link: 'admin.category.index', name: '分类'},
                {link: 'admin.tag.index', name: '标签'}
            ]
        },
        {
            index: 2,
            anchor: 'admin.user.index', icon: 'fa-user', title: '用户管理', 
            submenu: [
                {link: 'admin.me.index', name: '我的账户'},
                {
                    link: '#jsqx', name: '角色权限', sub: [
                        {sublink: 'admin.role.index', subname: '角色(用户组)'},
                        {sublink: 'admin.permission.index', subname: '权限'}
                    ]
                }
            ]
        },
        {
            index: 3,
            anchor: 'admin.business.index', icon: 'fa-coffee', title: '业务管理', 
            submenu: [
                {link: 'admin.flow', name: '业务流程'},
                {link: 'admin.customer.index', name: '我的客户'}
            ]
        },
        {
            index: 4,
            anchor: 'admin.system_option.index', icon: 'fa-cog', title: '系统管理',
            submenu: [
                {link: 'admin.system_option.index', name: '系统配置'},
                {
                    link: '#dtszgl', name: '动态设置', sub: [
                        {sublink: 'admin.setting_type.index', subname: '动态设置分组'},
                        {sublink: 'admin.setting.index', subname: '动态设置'}
                    ]
                },
                {link: 'admin.system_log.index', name: '系统日志'}
            ]
        }
    ],
    cur_top:0  //顶级导航高亮索引
};
var list = data.list;
var map = {};
var i = 0;
for(var listIndex in list){
    for(var submenuIndex in list[listIndex]['submenu']){
        map[i] = { 'url': list[listIndex]['submenu'][submenuIndex]['link'], 'index': listIndex };
        i++;
        if(list[listIndex]['submenu'][submenuIndex]['sub'] !== undefined){
            for(var subIndex in list[listIndex]['submenu'][submenuIndex]['sub'])
            {
                map[i] = { 'url': list[listIndex]['submenu'][submenuIndex]['sub'][subIndex]['sublink'], 'index': listIndex };
                i++;
            }
        }
    }
}
console.log(map);
    </script>
</body>
</html>
]]>
0 https://douyasi.com/javascript/json_foreach.html#comments https://douyasi.com/feed/javascript/json_foreach.html
JSONPath的使用 https://douyasi.com/javascript/jsonpath.html https://douyasi.com/javascript/jsonpath.html Mon, 02 Feb 2015 14:27:00 +0800 douyasi 某些情况下,我们需要查询json对象特定属性值下的结果,通过JSONPath可以很方便我们查询。

关于 JsonPath 的介绍,http://goessner.net/articles/JsonPath/JsonPath 对于 JSON 来说相当于 XPATH 对于 XML 。这是一个简单的从文档中抽取指定信息的工具,提供多种语言实现版本,包括:Javascript, Java, PythonPHP 等。

这里我要演示的是 javascript 实现版本,使用的 jsonpath 类库来自 GitHubjQuery-JSONPath ,注意该类库依赖于 jQuery

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONPath演示</title>
    <script type="text/javascript" src="jquery-1.8.3.min.js"></script>
    <script type="text/javascript" src="jsonpath.jquery.min.js"></script>
</head>
<body>
    <script type="text/javascript">
var json = {
  "store": {
    "book": [
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

var path = $.JSONPath({data: json, keepHistory: false});
var authors = path.query('$.store.book[*].author');  // returns all authors
var cheapier_books = path.query('$..book[?(@.price<10)]');  // filter all books cheapier than 10 
var isbn_books = path.query('$..book[?(@.isbn)]');  // filter all books with isbn number
var reference_books = path.query('$..book[?(@.category == "reference")]');  // filter 'reference' books
console.log(authors);
console.log(cheapier_books);
console.log(isbn_books);
console.log(reference_books);
    </script>
</body>
</html>
]]>
0 https://douyasi.com/javascript/jsonpath.html#comments https://douyasi.com/feed/javascript/jsonpath.html
Javascript获取当前URL相关参数 https://douyasi.com/javascript/js_url.html https://douyasi.com/javascript/js_url.html Wed, 03 Dec 2014 13:45:00 +0800 douyasi 演示代码:
var search = window.location.search; //获取url中"?"符后的字串
var hash = window.location.hash; //获取url中"#"锚点符
        
        var parser = document.createElement('a');
        //var parser = {};
        parser.href = "http://example.com:3000/pathname/?search=test#hash";
        parser.protocol; // => "http:"
        parser.hostname; // => "example.com"
        parser.port;     // => "3000"
        parser.pathname; // => "/pathname/"
        parser.search;   // => "?search=test"
        parser.hash;     // => "#hash"
        parser.host;     // => "example.com:3000"
        /*
        hash     从井号 (#) 开始的 URL(锚)
        host     主机名和当前 URL 的端口号
        hostname     当前 URL 的主机名
        href     完整的 URL
        pathname     当前 URL 的路径部分
        port     当前 URL 的端口号
        protocol     当前 URL 的协议
        search     从问号 (?) 开始的 URL(查询部分)
        */
    console.log(search);
    console.log(hash);
 

相关资源

解析URL的JS类库:

]]>
0 https://douyasi.com/javascript/js_url.html#comments https://douyasi.com/feed/javascript/js_url.html
JavaScript学习笔记03 —— 异常捕获与处理 https://douyasi.com/javascript/js_03.html https://douyasi.com/javascript/js_03.html Thu, 27 Nov 2014 16:00:00 +0800 douyasi 知识要点

throw 用于显式抛出异常

function factorial(x){
    if( x < 0) throw new Error("x 不能为负数!");
    for (var f = 1; x > 1; f *= x, x--) 
    return f;
}
factorial(-2);

执行之后,查看浏览器console栏会看到下面错误:

20141128165740.jpg

try/catch/finally 语句

function check_mobile(mobile){
    try{
        if( /^13[0-9]{9}|14[57]{1}[0-9]{8}|15[012356789]{1}[0-9]{8}|170[059]{1}[0-9]{8}|18[0-9]{9}$/.test(mobile) ){
            console.log("手机号" + mobile + "正确!");
        }
        else throw new Error("手机号" + mobile + "非法!");
    }
    catch(ex){
        alert(ex);
    }
    finally{
        console.log('检查手机号方法已执行!');
    }
}
check_mobile('1234567890');
check_mobile('1388888888888');

不管try块中是否产生异常,finally块内的代码逻辑总会执行。

]]>
0 https://douyasi.com/javascript/js_03.html#comments https://douyasi.com/feed/javascript/js_03.html
js处理时间戳 https://douyasi.com/javascript/js_timestamp.html https://douyasi.com/javascript/js_timestamp.html Mon, 10 Nov 2014 15:13:00 +0800 douyasi 废话少说,直接上代码。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JS timestamp</title>
</head>
<body>
    <script type="text/javascript">
        function formatDate(ts) {
            var now = new Date(parseInt(ts) * 1000);
            console.log(now);
            var year = now.getFullYear();
            var month = ((now.getMonth()+1)<10)?('0'+(now.getMonth()+1)):(now.getMonth()+1);
            var date = (now.getDate()<10)?('0'+now.getDate()):(now.getDate());
            var hour = (now.getHours()<10)?('0'+now.getHours()):(now.getHours());
            var minute = (now.getMinutes()<10)?('0'+now.getMinutes()):(now.getMinutes());
            var second = (now.getSeconds()<10)?('0'+now.getSeconds()):(now.getSeconds());
            return   year+"-"+month+"-"+date+"   "+hour+":"+minute+":"+second;
        }
        console.log(formatDate(1415433769));
    </script>
</body>
</html>

引申知识:

getYear() 获取的年份为"当前年份-1900" 的值,使用 getFullYear() 能获取到完整的年份值。

]]>
0 https://douyasi.com/javascript/js_timestamp.html#comments https://douyasi.com/feed/javascript/js_timestamp.html
RunJS https://douyasi.com/javascript/run_js.html https://douyasi.com/javascript/run_js.html Thu, 30 Oct 2014 20:43:00 +0800 douyasi

RunJS 是一个在线的 HTML、Javascript、CSS 等 web 前端代码的编辑分享平台,拥有实时预览、高亮显示、代码格式化等功能。

官方网址:http://runjs.cn/

下面列出一些有趣的js代码,可以直接在线浏览演示:

]]>
0 https://douyasi.com/javascript/run_js.html#comments https://douyasi.com/feed/javascript/run_js.html
markdown编辑器 https://douyasi.com/javascript/web_markdown_editor.html https://douyasi.com/javascript/web_markdown_editor.html Tue, 28 Oct 2014 12:24:00 +0800 douyasi 一直想找一个基于 webmarkdown 编辑器,使用过几个,一直不如意。实际来说,Typechomarkdown 编辑器已经很不错了,一直想把它弄下来放在自己项目,奈何,其相关js代码已压缩或有其它依赖,在没有任何文档的情况下,修改起来很麻烦。

最后,我找到一个名为 EpicEditor 的js嵌入式编辑器,使用了一下,很方便。下面是 EpicEditor 官网自我介绍:

EpicEditor is an embeddable JavaScript Markdown editor with split fullscreen editing, live previewing, automatic draft saving, offline support, and more. For developers, it offers a robust API, can be easily themed, and allows you to swap out the bundled Markdown parser with anything you throw at it.

使用后,发现 EpicEditor 不支持IE9以下浏览器,所以,如果你的网站如果需要还支援IE 8浏览器,这个markdown编辑器可能不太适合你。

EpicEditor官网:http://epiceditor.com/

发现一个更好的,大力推荐使用 editor.md

]]>
0 https://douyasi.com/javascript/web_markdown_editor.html#comments https://douyasi.com/feed/javascript/web_markdown_editor.html
JavaScript学习笔记02 —— 表达式和运算符 https://douyasi.com/javascript/js_02.html https://douyasi.com/javascript/js_02.html Thu, 23 Oct 2014 16:31:00 +0800 douyasi

本系列为阅读《JavaScript权威指南》之后所做的笔记,只供个人学习与参考。

知识要点

  • 一些原始表达式
    1.23  //数字直接量
    "hello"  //字符串直接量
    /pattern/  //正则表达式直接量
    
    true  //布尔值:真
    false  //布尔值:假
    null  //空
    this  //返回“当前”对象
    
    i  //变量“i”
    sum  
    undefined  //undefined是全局变量,和null不同,它不是一个关键字
  • 下面数组包含5个元素,其中三个元素是undefined
var sparseArray = [1,,,,5];
  • 对象初始化
var p = {x:2.3,y:-1.2};//一个拥有两个属性成员的对象
var q = {}; //空对象
  • 函数定义表达式
var square = function(x) { return x*x; }
  • 属性访问表达式
var o = {x:1,y:{z:3}}; // An example object
var a = [o,4,[5,6]]; // An example array that contains the object
o.x // => 1: property x of expression o
o.y.z // => 3: property z of expression o.y
o["x"] // => 1: property x of object o
a[1] // => 4: element at index 1 of expression a
a[2]["1"] // => 6: element at index 1 of expression a[2]
a[0].x // => 1: property x of expression a[0]
  • 对象创建表达式
new Object()
new Point(2,3)
  • 运算符及其优先级

内容过多,略去。这里特别注意下位运算符:与(&) 、 或(|) 、 非(~) 、异或(^) 、 左移(<<) 、 右移(>>)。

一些示例

===== 区别

恒等于(===)一般要求比较的两者值和类型都要相同。
测试用例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>比较运算测试</title>
</head>
<body>
    <script type="text/javascript">
    console.log(null===undefined);  //false
    console.log(true===1);  //false
    console.log(NaN===NaN);  //false
    console.log(NaN!==NaN);  //true
    console.log(0===-0);  //true
    console.log(0xf===16);  //false
    </script>
</body>
</html>

NaN 属性是代表非数字值的特殊值。该属性用于指示某个值不是数字。可以把 Number 对象设置为该值,来指示其不是数字值。

eval

eval("3+2");   //5
var geval = eval; // Using another name does a global eval
var x = "global", y = "global"; // Two global variables
function f() { // This function does a local eval
var x = "local"; // Define a local variable
eval("x += 'changed';"); // Direct eval sets local variable
return x; // Return changed local variable
}
function g() { // This function does a global eval
var y = "local"; // A local variable
geval("y += 'changed';"); // Indirect eval sets global variable
return y; // Return unchanged local variable
}
console.log(f(), x); // Local variable changed: prints "localchanged global":
console.log(g(), y); // Global variable changed: prints "local globalchanged":
]]>
0 https://douyasi.com/javascript/js_02.html#comments https://douyasi.com/feed/javascript/js_02.html